Repository: jbranchaud/til Branch: master Commit: 329ce1aa3e20 Files: 1768 Total size: 1.7 MB Directory structure: gitextract_4xrtz725/ ├── .gitmodules ├── .vimrc ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── Taskfile.yml ├── ack/ │ ├── ack-bar.md │ ├── case-insensitive-search.md │ └── list-available-file-types.md ├── ansible/ │ └── loop-over-a-list-of-dictionaries.md ├── astro/ │ ├── generate-types-for-a-content-collection.md │ └── markdown-files-are-of-type-markdown-instance.md ├── aws/ │ ├── aws-cli-requires-groff-executable.md │ ├── find-and-follow-server-logs.md │ ├── list-rds-snapshots-with-matching-identifier-prefix.md │ ├── output-cli-results-in-different-formats.md │ ├── sign-up-user-with-email-and-password.md │ ├── ssh-into-an-ecs-container.md │ ├── turn-off-output-pager-for-a-command.md │ └── use-specific-aws-profile-with-cli.md ├── bash/ │ └── edit-the-current-command-prompt.md ├── brew/ │ ├── clean-up-your-brew-installations.md │ ├── configure-brew-environment-variables.md │ ├── export-list-of-everything-installed-by-brew.md │ ├── install-from-nonstandard-brewfile.md │ ├── install-go-packages-in-brewfile.md │ └── list-all-services-managed-by-brew.md ├── chrome/ │ ├── access-a-value-logged-to-the-console.md │ ├── chrome-supports-many-unix-keyboard-shortcuts.md │ ├── copy-some-data-from-the-console.md │ ├── duplicate-the-current-tab.md │ ├── easier-access-to-network-throttling-controls.md │ ├── keybinding-to-focus-the-address-bar.md │ ├── navigate-the-browser-history-with-vimium.md │ ├── open-current-tab-in-new-window-with-vimium.md │ ├── pause-javascript-from-the-source-devtools-panel.md │ ├── pretty-print-tabular-data.md │ ├── reference-the-selected-node.md │ ├── search-tabs-with-the-vimium-vomnibar.md │ ├── selecting-dom-elements-faster-than-ever.md │ ├── simulating-various-connection-speeds.md │ ├── toggle-device-mode.md │ ├── toggle-open-the-console-drawer.md │ ├── trigger-commands-from-the-devtools-command-palette.md │ └── view-network-traffic-for-new-tabs.md ├── claude-code/ │ ├── allow-edits-from-the-start.md │ ├── monitor-usage-limits-from-cli.md │ ├── open-current-prompt-in-default-editor.md │ └── resume-specific-session.md ├── clojure/ │ ├── aggregation-using-merge-with.md │ ├── argument-requirements-for-a-function.md │ ├── combinations-of-items-from-a-sequence.md │ ├── define-something-only-once.md │ ├── evaluate-one-liners-with-lein-exec.md │ ├── expanding-macros.md │ ├── get-the-value-of-an-environment-variable.md │ ├── list-functions-for-a-namespace.md │ ├── load-a-file-into-the-repl.md │ ├── mapping-with-an-index.md │ ├── open-javadocs.md │ ├── pretty-print-the-last-thing.md │ ├── quick-clojure-docs.md │ ├── reductions.md │ ├── set-max-heap-size.md │ ├── specify-the-directory-of-a-shell-command.md │ ├── splitting-on-whitespace.md │ ├── swap-two-items-in-a-vector.md │ ├── try-a-clojure-project-in-the-repl.md │ ├── type-of-anything.md │ └── when-overflow-is-desired.md ├── css/ │ ├── add-fab-icons-to-your-site-with-fontawesome-5.md │ ├── add-line-numbers-to-a-code-block-with-counter.md │ ├── animate-smoothly-between-two-background-colors.md │ ├── apply-multiple-box-shadows-to-single-element.md │ ├── apply-styles-based-on-dark-mode-preferences.md │ ├── apply-styles-to-the-last-child-of-a-specific-type.md │ ├── change-the-orientation-of-an-image.md │ ├── circular-icons-with-a-massive-border-radius.md │ ├── clean-up-repetition-with-is-pseudo-class.md │ ├── conditional-styling-for-unsupported-css-features.md │ ├── create-a-pulsing-background-with-css-animation.md │ ├── define-css-custom-properties-with-scss-variables.md │ ├── define-hsl-colors-with-alpha-values.md │ ├── display-responsive-iframe-maintaining-aspect-ratio.md │ ├── dry-up-scss-with-mixins.md │ ├── filter-blur-requires-expensive-calculation.md │ ├── give-elements-the-same-width-with-flexbox.md │ ├── let-pointer-events-pass-through-an-element.md │ ├── lighten-and-darken-with-css-brightness-filter.md │ ├── lighten-and-darken-with-scss.md │ ├── make-a-block-of-text-respect-new-lines.md │ ├── parameterized-scss-mixins.md │ ├── prevent-invisible-elements-from-being-clicked.md │ ├── root-has-higher-specificity-than-html.md │ ├── style-a-background-with-a-linear-gradient.md │ └── using-maps-in-scss.md ├── cursor/ │ └── allow-cursor-to-be-launched-from-cli.md ├── deno/ │ └── read-in-the-contents-of-a-file.md ├── devops/ │ ├── aliasing-an-ansible-host.md │ ├── allow-cross-origin-requests-to-include-cookies.md │ ├── allow-https-through-your-ufw-firewall.md │ ├── check-for-cached-site-association-file-for-ios.md │ ├── check-the-status-of-all-services.md │ ├── check-the-syntax-of-nginx-files.md │ ├── connect-to-an-rds-postgresql-database.md │ ├── default-rails-deploy-script-on-hatchbox.md │ ├── determine-the-ip-address-of-a-domain.md │ ├── hatchbox-exports-env-vars-with-asdf.md │ ├── path-of-the-packets.md │ ├── push-non-master-branch-to-heroku.md │ ├── reload-the-nginx-configuration.md │ ├── resolve-the-public-ip-of-a-url.md │ ├── running-out-of-inode-space.md │ ├── set-get-and-unset-env-vars-with-dokku.md │ ├── set-up-domain-for-hatchbox-rails-app.md │ ├── ssh-into-a-docker-container.md │ ├── ssl-certificates-can-cover-multiple-domains.md │ └── wipe-a-heroku-postgres-database.md ├── docker/ │ ├── check-postgres-version-running-in-docker-container.md │ ├── configure-different-host-and-container-ports.md │ ├── list-running-docker-containers.md │ ├── prevent-containers-from-running-on-startup.md │ ├── run-a-basic-postgresql-server-in-docker.md │ └── run-sql-script-against-postgres-container.md ├── dprint.json ├── drizzle/ │ ├── create-bigint-identity-column-for-primary-key.md │ ├── drizzle-tracks-migrations-in-a-log-table.md │ └── get-fields-for-inserted-row.md ├── elixir/ │ ├── all-values-for-a-key-in-a-keyword-list.md │ ├── append-to-a-keyword-list.md │ ├── assert-an-exception-is-raised.md │ ├── binary-representation-of-a-string.md │ ├── check-for-a-substring-match.md │ ├── check-list-membership.md │ ├── comparing-datetime-structs.md │ ├── compute-intermediate-values-in-a-with-construct.md │ ├── compute-md5-digest-of-a-string.md │ ├── counting-records-with-ecto.md │ ├── create-a-date-with-the-date-sigil.md │ ├── create-a-list-of-atoms.md │ ├── creating-a-pid.md │ ├── creating-indexes-with-ecto.md │ ├── defining-multiple-clauses-in-an-anonymous-function.md │ ├── determine-the-latest-release-of-a-hex-package.md │ ├── do-you-have-the-time-part-2.md │ ├── do-you-have-the-time.md │ ├── documentation-lookup-with-vim-and-alchemist.md │ ├── dynamically-generating-atoms.md │ ├── execute-raw-sql-in-an-ecto-migration.md │ ├── expose-internal-representation.md │ ├── include-captures-with-string-split.md │ ├── inspecting-the-process-message-queue.md │ ├── list-functions-for-a-module.md │ ├── listing-files-in-iex.md │ ├── match-on-a-map-in-a-with-construct.md │ ├── passing-around-and-using-modules.md │ ├── pattern-matching-in-anonymous-functions.md │ ├── pipe-into-a-case-statement.md │ ├── quitting-iex.md │ ├── range-into-list-using-comprehensions.md │ ├── refer-to-a-module-within-itself.md │ ├── referencing-values-in-iexs-history.md │ ├── remove-one-list-from-another.md │ ├── replace-duplicates-in-a-keyword-list.md │ ├── requiring-keys-for-structs.md │ ├── reversing-a-list-part-2.md │ ├── reversing-a-list.md │ ├── root-directory-of-a-project.md │ ├── round-floats-to-integers.md │ ├── run-exunit-tests-in-a-deterministic-order.md │ ├── run-the-test-at-a-specific-line-number.md │ ├── same-functions-should-be-grouped-together.md │ ├── skip-a-specific-test.md │ ├── string-interpolation-with-just-about-anything.md │ ├── unique-indexes-with-ecto.md │ ├── updating-values-in-a-map.md │ ├── using-when-clauses-in-a-with-construct.md │ ├── virtual-fields-with-ecto-schemas.md │ ├── when-things-dont-match-the-with-statements.md │ └── word-lists-for-atoms.md ├── gatsby/ │ └── add-javascript-to-body-of-the-document.md ├── git/ │ ├── accessing-a-lost-commit.md │ ├── add-a-range-of-filenames-to-gitignore.md │ ├── add-only-tracked-files-from-a-directory.md │ ├── amend-author-of-previous-commit.md │ ├── auto-squash-those-fixup-commits.md │ ├── better-diffs-with-delta.md │ ├── caching-credentials.md │ ├── change-the-start-point-of-a-branch.md │ ├── check-how-a-file-is-being-ignored.md │ ├── check-if-a-file-has-changed-in-a-script.md │ ├── check-if-a-file-is-under-version-control.md │ ├── checking-commit-ancestry.md │ ├── checkout-old-version-of-a-file.md │ ├── checkout-previous-branch.md │ ├── cherry-pick-a-range-of-commits.md │ ├── cherry-pick-multiple-commits-at-once.md │ ├── clean-out-all-local-branches.md │ ├── clean-out-working-copy-with-patched-restore.md │ ├── clean-up-old-remote-tracking-references.md │ ├── clear-entries-from-git-stash.md │ ├── clone-a-repo-just-for-the-files-without-history.md │ ├── clone-a-repo-locally-from-git.md │ ├── configure-global-gitignore-file.md │ ├── configuring-the-pager.md │ ├── copy-a-file-from-another-branch.md │ ├── count-all-files-of-specific-type-tracked-by-git.md │ ├── count-number-of-commits-on-a-branch.md │ ├── create-a-new-branch-with-git-switch.md │ ├── delete-all-untracked-files.md │ ├── determine-absolute-path-of-top-level-project-directory.md │ ├── determine-the-hash-id-for-a-blob.md │ ├── diffing-with-patience.md │ ├── dropping-commits-with-git-rebase.md │ ├── dry-runs-in-git.md │ ├── exclude-a-directory-during-a-command.md │ ├── exclude-a-file-from-a-diff-output.md │ ├── excluding-files-locally.md │ ├── extend-git-with-custom-commands.md │ ├── files-with-local-changes-cannot-be-removed.md │ ├── find-and-remove-files-that-match-a-name.md │ ├── find-the-date-that-a-file-was-added-to-the-repo.md │ ├── find-the-initial-commit.md │ ├── fix-whitespace-errors-throughout-branch-commits.md │ ├── get-latest-commit-timestamp-for-a-file.md │ ├── get-the-name-of-the-current-branch.md │ ├── get-the-short-version-of-the-latest-commit.md │ ├── grab-a-single-file-from-a-stash.md │ ├── grep-for-a-pattern-on-another-branch.md │ ├── grep-over-commit-messages.md │ ├── highlight-extra-whitespace-in-diff-output.md │ ├── highlight-small-change-on-single-line.md │ ├── ignore-changes-to-a-tracked-file.md │ ├── ignore-files-specific-to-your-workflow.md │ ├── include-a-message-with-your-stashed-changes.md │ ├── include-or-exclude-remaining-patch-changes.md │ ├── include-some-stats-in-your-git-log.md │ ├── intent-to-add.md │ ├── interactively-checkout-specific-files-from-a-stash.md │ ├── interactively-unstage-changes.md │ ├── keep-file-locally-with-git-rm.md │ ├── last-commit-a-file-appeared-in.md │ ├── list-all-files-added-during-span-of-time.md │ ├── list-all-files-changed-between-two-branches.md │ ├── list-all-git-aliases-from-gitconfig.md │ ├── list-branches-that-contain-a-commit.md │ ├── list-commits-on-a-branch.md │ ├── list-different-commits-between-two-branches.md │ ├── list-filenames-without-the-diffs.md │ ├── list-just-the-files-involved-in-a-commit.md │ ├── list-most-git-commands.md │ ├── list-untracked-files-for-scripting.md │ ├── list-untracked-files.md │ ├── move-the-latest-commit-to-a-new-branch.md │ ├── override-the-global-git-ignore-file.md │ ├── pick-specific-changes-to-stash.md │ ├── pulling-in-changes-during-an-interactive-rebase.md │ ├── push-to-a-branch-on-another-remote.md │ ├── quicker-commit-fixes-with-the-fixup-flag.md │ ├── rebase-commits-with-an-arbitrary-command.md │ ├── reference-a-commit-via-commit-message-pattern-matching.md │ ├── reference-commits-earlier-than-reflog-remembers.md │ ├── remove-untracked-files-from-a-directory.md │ ├── rename-a-remote.md │ ├── renaming-a-branch.md │ ├── resetting-a-reset.md │ ├── resolve-a-merge-conflict-from-stash-pop.md │ ├── restore-file-from-one-branch-to-the-current.md │ ├── review-commits-from-before-a-certain-date.md │ ├── run-a-git-command-from-outside-the-repo.md │ ├── set-a-custom-pager-for-a-specific-command.md │ ├── set-default-branch-name-for-new-repos.md │ ├── set-up-gpg-signing-key.md │ ├── shorthand-to-force-push-a-branch.md │ ├── show-all-commits-for-a-file-beyond-renaming.md │ ├── show-changes-for-files-that-match-a-pattern.md │ ├── show-changes-in-the-compose-commit-message-view.md │ ├── show-file-diffs-when-viewing-git-log.md │ ├── show-list-of-most-recently-committed-branches.md │ ├── show-only-commits-that-touch-specific-lines.md │ ├── show-summary-stats-for-current-branch.md │ ├── show-the-diffstat-summary-of-a-commit.md │ ├── show-the-good-and-the-bad-with-git-bisect.md │ ├── show-what-is-in-a-stash.md │ ├── single-key-presses-in-interactive-mode.md │ ├── skip-a-bad-commit-when-bisecting.md │ ├── skip-git-hooks-as-needed.md │ ├── skip-pre-commit-hooks.md │ ├── staging-changes-within-vim.md │ ├── staging-stashes-interactively.md │ ├── stash-a-single-untracked-file.md │ ├── stash-everything.md │ ├── stashing-only-unstaged-changes.md │ ├── stashing-untracked-files.md │ ├── switch-to-a-recent-branch-with-fzf.md │ ├── transition-a-branch-from-one-base-to-another.md │ ├── turn-off-the-output-pager-for-one-command.md │ ├── two-kinds-of-dotted-range-notation.md │ ├── undo-latest-changes-committed-to-specific-file.md │ ├── unstage-changes-with-git-restore.md │ ├── untrack-a-directory-of-files-without-deleting.md │ ├── untrack-a-file-without-deleting-it.md │ ├── update-the-url-of-a-remote.md │ ├── use-external-diff-tool-like-difftastic.md │ ├── using-commands-with-a-relative-date-format.md │ ├── verbose-commit-message.md │ ├── viewing-a-file-on-another-branch.md │ ├── what-changed.md │ ├── what-is-the-current-branch.md │ └── whitespace-warnings.md ├── github/ │ ├── access-your-github-profile-photo.md │ ├── open-a-pr-to-an-unforked-repo.md │ ├── target-another-repo-when-creating-a-pr.md │ └── tell-gh-what-the-default-repo-is.md ├── github-actions/ │ ├── cache-playwright-dependencies-across-workflows.md │ ├── capture-an-output-value-for-use-in-a-later-step.md │ ├── disable-a-workflow-with-the-gh-cli.md │ ├── reference-an-encrypted-secret-in-an-action.md │ ├── trigger-a-workflow-via-an-api-call.md │ └── use-labels-to-block-pr-merge.md ├── go/ │ ├── access-go-docs-offline.md │ ├── add-a-method-to-a-struct.md │ ├── basic-delve-debugging-session.md │ ├── build-for-a-specific-os-and-architecture.md │ ├── check-if-cobra-flag-was-set.md │ ├── combine-two-slices.md │ ├── configure-max-string-print-length-for-delve.md │ ├── connect-to-a-sqlite-database.md │ ├── create-a-slice-from-an-array.md │ ├── detect-if-stdin-comes-from-a-redirect.md │ ├── deterministically-seed-a-random-number-generator.md │ ├── difference-between-slice-and-pointer-to-slice.md │ ├── do-something-n-times.md │ ├── find-executables-installed-by-go.md │ ├── format-date-and-time-with-time-constants.md │ ├── not-so-random.md │ ├── parse-a-string-into-individual-fields.md │ ├── parse-flags-from-cli-arguments.md │ ├── pass-a-struct-to-a-function.md │ ├── produce-the-zero-value-of-a-generic-type.md │ ├── redirect-file-to-stdin-during-delve-debug.md │ ├── replace-the-current-process-with-an-external-command.md │ ├── sleep-for-a-duration.md │ ├── sort-slice-in-ascending-or-descending-order.md │ ├── upgrading-from-an-older-version-on-mac.md │ └── write-a-custom-scan-function-for-file-io.md ├── groq/ │ ├── grab-multiple-values-from-a-reference.md │ ├── grab-values-from-an-array-of-references.md │ ├── include-attributes-when-conditional-check-passes.md │ └── include-type-of-operation-in-webhook-response.md ├── heroku/ │ ├── check-ruby-version-for-production-app.md │ ├── connect-to-a-database-by-color.md │ ├── deploy-a-review-app-to-a-different-stack.md │ ├── diagnose-problems-in-a-heroku-postgres-database.md │ ├── open-dashboard-for-specific-add-on.md │ ├── run-sql-against-remote-postgres-database.md │ ├── set-and-show-heroku-env-variables.md │ ├── specify-default-team-and-app-for-project.md │ └── ssh-into-heroku-server-hosting-app.md ├── html/ │ ├── adding-alt-text-to-an-image.md │ ├── allow-number-input-to-accept-decimal-values.md │ ├── determine-which-button-submitted-the-form.md │ ├── disable-auto-completion-for-a-form-input.md │ ├── disclose-additional-details.md │ ├── make-elements-non-interactive-with-inert.md │ ├── prevent-search-engines-from-indexing-a-page.md │ ├── render-text-as-superscript.md │ └── submit-a-form-with-a-button-outside-the-form.md ├── http/ │ └── what-counts-as-cross-origin-with-cors.md ├── inngest/ │ ├── ensure-lookup-can-be-retried.md │ └── exit-function-early-without-retries.md ├── internet/ │ ├── add-emoji-to-github-repository-description.md │ ├── add-styled-alerts-to-github-markdown-documents.md │ ├── analyze-your-website-performance.md │ ├── check-your-public-ip-address.md │ ├── digraph-unicode-characters-have-a-titlecase.md │ ├── download-a-google-doc-as-specific-format.md │ ├── enable-keyboard-shortcuts-in-gmail.md │ ├── exclude-ai-overview-from-google-search.md │ ├── exclude-whitespace-changes-from-github-diffs.md │ ├── figure-out-your-public-ip-address.md │ ├── focus-the-url-bar.md │ ├── get-random-images-from-unsplash.md │ ├── grab-the-rss-feed-for-a-substack-blog.md │ ├── hide-overflowing-text-for-google-sheets-column.md │ ├── search-tweets-by-author.md │ ├── show-all-pivotal-stories-with-blockers.md │ └── verify-site-ownership-with-dns-record.md ├── java/ │ ├── ensure-resources-always-get-closed.md │ ├── install-java-on-mac-with-brew.md │ └── run-a-hello-world-program-in-eclipse.md ├── javascript/ │ ├── accessing-arguments-to-a-function.md │ ├── add-item-to-an-array-of-references-in-sanity.md │ ├── basic-date-formatting-without-a-library.md │ ├── character-codes-from-keyboard-listeners.md │ ├── check-classes-on-a-dom-element.md │ ├── check-if-a-number-is-positive-or-negative.md │ ├── check-if-file-exists-before-reading-it.md │ ├── check-if-something-is-an-array.md │ ├── check-media-queries-from-javascript.md │ ├── check-the-password-confirmation-with-yup.md │ ├── compare-the-equality-of-two-date-objects.md │ ├── computed-property-names-in-es6.md │ ├── conditionally-include-pairs-in-an-object.md │ ├── configure-jest-to-run-a-test-setup-file.md │ ├── convert-seconds-to-date-object.md │ ├── create-a-cancelable-promise-with-pcancelable.md │ ├── create-an-array-containing-1-to-n.md │ ├── create-an-object-with-no-properties.md │ ├── create-bootstrapped-apps-with-yarn.md │ ├── create-future-and-past-dates-from-today.md │ ├── custom-type-checking-error-messages-with-yup.md │ ├── default-and-named-exports-from-the-same-module.md │ ├── define-a-custom-jest-matcher.md │ ├── destructure-with-access-to-nested-value-and-parent-value.md │ ├── destructuring-the-rest-of-an-array.md │ ├── easy-date-comparison-with-dayjs.md │ ├── enable-es7-transforms-with-react-rails.md │ ├── ensure-shell-can-find-global-npm-binaries.md │ ├── expand-emojis-with-the-spread-operator.md │ ├── fill-an-input-with-a-ton-of-text.md │ ├── find-the-version-of-an-installed-dependency.md │ ├── find-where-yarn-is-installing-binaries.md │ ├── for-in-iterates-over-object-properties.md │ ├── format-a-decimal-to-a-fixed-number-of-digits.md │ ├── format-a-list-of-items-by-locale.md │ ├── format-time-zone-identifier.md │ ├── formatting-values-with-units-for-display.md │ ├── freeze-an-object-sorta.md │ ├── generate-a-v4-uuid-in-the-browser.md │ ├── generate-random-integers.md │ ├── get-the-location-and-size-of-an-element.md │ ├── get-the-response-status-from-an-axios-error.md │ ├── get-the-time-components-of-a-date.md │ ├── get-the-time-zone-of-the-client-computer.md │ ├── globally-install-a-package-with-yarn.md │ ├── globally-install-specific-version-of-pnpm.md │ ├── immutable-remove-with-the-spread-operator.md │ ├── initialize-a-new-javascript-project-with-yarn.md │ ├── install-the-latest-version-of-node-with-nvm.md │ ├── interpolate-a-string-into-a-regex.md │ ├── iso-8601-formatted-dates-are-interpreted-as-utc.md │ ├── link-a-javascript-package-locally.md │ ├── list-top-level-npm-dependencies.md │ ├── load-and-use-env-var-in-node-script.md │ ├── make-the-browser-editable-with-design-mode.md │ ├── make-truly-deep-clone-with-structured-clone.md │ ├── matching-a-computed-property-in-function-args.md │ ├── matching-multiple-values-in-a-switch-statement.md │ ├── mock-a-function-with-return-values-using-jest.md │ ├── new-dates-can-take-out-of-bounds-values.md │ ├── numbers-are-empty.md │ ├── object-initialization-with-shorthand-property-names.md │ ├── obtain-undefined-value-with-the-void-operator.md │ ├── open-global-npm-config-file.md │ ├── parse-a-date-from-a-timestamp.md │ ├── pre-and-post-hooks-for-yarn-scripts.md │ ├── prevent-hidden-element-from-flickering-on-load.md │ ├── purge-null-and-undefined-values-from-object.md │ ├── random-cannot-be-seeded.md │ ├── reach-into-an-object-for-nested-data-with-get.md │ ├── render-an-array-of-elements-with-react-16.md │ ├── resolve-and-pass-multiple-values-from-a-then.md │ ├── run-a-bash-script-from-a-node-script.md │ ├── run-multiple-node-scripts-concurrently.md │ ├── running-es6-specs-with-mocha.md │ ├── scoping-variables-with-a-block-statement.md │ ├── short-circuit-concurrently-when-process-fails.md │ ├── show-description-of-all-npm-config-options.md │ ├── sleep-for-a-bit-in-async-code.md │ ├── sorting-arrays-of-objects-with-lodash.md │ ├── splat-arguments-to-a-function.md │ ├── spread-merging-objects-includes-nil-values.md │ ├── spread-the-rest-with-es6.md │ ├── start-node-process-in-specific-timezone.md │ ├── string-interpolation-with-template-literals.md │ ├── support-nested-matching-in-custom-jest-matchers.md │ ├── tell-jest-to-focus-on-running-only-one-test.md │ ├── tell-node-to-treat-js-files-as-esm.md │ ├── tell-prettier-to-not-format-a-statement.md │ ├── test-coverage-stats-with-jest.md │ ├── test-timing-based-code-with-jest-fake-timers.md │ ├── the-comma-operator.md │ ├── throttling-a-function-call.md │ ├── timing-processes.md │ ├── transforming-es6-and-jsx-with-babel-6.md │ ├── truthiness-of-integer-arrays.md │ ├── turn-an-html-collection-into-an-array.md │ ├── turn-off-console-error-messages-in-a-test.md │ ├── turn-off-npm-funding-message.md │ ├── waiting-on-multiple-promises.md │ ├── who-am-i-npm-edition.md │ ├── write-a-javascript-object-to-a-json-file.md │ ├── yarn-commands-without-the-emojis.md │ └── yup-schemas-are-validated-asynchronously.md ├── jj/ │ ├── colocate-jj-and-git-directories-for-project.md │ ├── describe-current-changes-and-create-new-change.md │ ├── find-system-wide-config-file-for-user.md │ └── squash-changes-into-parent-commit-interactively.md ├── jq/ │ ├── combine-an-array-of-objects-into-a-single-object.md │ ├── count-each-collection-in-a-json-object.md │ ├── count-the-number-of-things-in-a-json-file.md │ ├── extract-a-list-of-values.md │ ├── filter-out-results-based-on-list-of-values.md │ ├── find-all-objects-in-an-array-where-key-is-set.md │ ├── find-all-objects-with-a-matching-key-value-pair.md │ ├── get-a-slice-of-the-ends-of-an-array.md │ ├── get-the-first-item-for-every-top-level-key.md │ ├── get-the-last-item-from-an-array.md │ ├── reduce-object-to-just-entries-of-a-specific-type.md │ ├── turn-a-list-from-a-command-into-json.md │ └── zip-two-json-files-together-based-on-shared-id.md ├── kitty/ │ ├── set-the-title-of-a-window.md │ └── use-the-built-in-emoji-picker.md ├── linux/ │ ├── check-ubuntu-version.md │ ├── configure-your-server-timezone.md │ ├── list-the-statuses-of-all-upstart-jobs.md │ ├── show-current-system-time-and-settings.md │ ├── show-used-and-available-system-memory.md │ └── upgrading-ubuntu.md ├── llm/ │ ├── send-curl-to-claude-text-completion-api.md │ └── use-the-llm-cli-with-claude-models.md ├── mac/ │ ├── access-all-screen-and-video-capture-options.md │ ├── access-system-information-on-osx.md │ ├── access-unsupported-screen-resolutions-with-rdm.md │ ├── add-a-bunch-of-cli-utilities-with-coreutils.md │ ├── capture-screenshot-to-clipboard-from-cli.md │ ├── check-network-quality-stats-from-the-command-line.md │ ├── clean-up-old-homebrew-files.md │ ├── control-which-monitor-app-switcher-appears-on.md │ ├── convert-an-heic-image-file-to-jpg.md │ ├── default-screenshot-location.md │ ├── detect-how-long-a-user-has-been-idle.md │ ├── disable-swipe-navigation-for-a-specific-app.md │ ├── display-a-message-with-alfred.md │ ├── find-the-process-using-a-specific-port.md │ ├── gesture-for-viewing-all-windows-of-current-app.md │ ├── insert-a-non-breaking-space-character.md │ ├── inspect-assertions-preventing-sleep.md │ ├── keyboard-shortcuts-for-interacting-with-text-areas.md │ ├── launch-some-confetti.md │ ├── list-all-the-say-voices.md │ ├── open-finder-app-to-specific-directory.md │ ├── prevent-sleep-with-the-caffeinate-command.md │ ├── quickly-type-en-dashes-and-em-dashes.md │ ├── require-additional-js-libraries-in-postman.md │ ├── resize-app-windows-with-applescript.md │ ├── resizing-both-corners-of-a-window.md │ ├── reveal-location-of-file-in-finder-app.md │ ├── run-a-hardware-check.md │ ├── run-applescript-commands-inline-in-the-terminal.md │ ├── set-a-window-to-its-default-zoom-level.md │ ├── specify-app-when-opening-from-command-line.md │ ├── start-amphetamine-session-with-applescript.md │ ├── uninstall-logitech-g-hub-from-mac.md │ ├── use-a-different-font-with-iterm2.md │ ├── use-default-screenshot-shortcuts-with-cleanshot-x.md │ ├── view-all-windows-of-the-current-app.md │ └── write-system-clipboard-to-a-file.md ├── mise/ │ ├── create-umbrella-task-for-all-test-tasks.md │ ├── list-the-files-being-loaded-by-mise.md │ ├── look-in-ruby-version-dotfile.md │ ├── override-your-project-mise-file.md │ ├── pick-from-tasks-using-interactive-picker.md │ ├── preserve-color-output-for-task-command.md │ ├── read-existing-dot-env-file-into-env-vars.md │ ├── run-a-command-with-specific-tool-version.md │ └── search-through-bin-paths-for-tool-locations.md ├── mongodb/ │ ├── determine-the-database-version.md │ ├── dump-a-remote-database.md │ ├── dump-and-restore-with-a-single-gzip-file.md │ ├── get-size-stats-for-a-collection.md │ └── list-size-stats-for-all-collections.md ├── mysql/ │ ├── change-existing-column-to-not-null.md │ ├── connect-to-a-database-in-safe-update-mode.md │ ├── default-username-and-password-for-new-instance.md │ ├── display-output-in-a-vertical-format.md │ ├── doing-date-math.md │ ├── dump-a-database-to-a-file.md │ ├── echo-a-message-from-a-sql-file.md │ ├── get-idea-of-what-is-in-a-json-column.md │ ├── ignore-duplicates-when-inserting-records.md │ ├── list-databases-and-tables.md │ ├── run-statements-in-a-transaction.md │ ├── select-rows-after-an-offset.md │ ├── set-value-on-null-json-column.md │ ├── show-create-statement-for-a-table.md │ ├── show-indexes-for-a-table.md │ └── show-tables-that-match-a-pattern.md ├── neovim/ │ ├── allow-neovim-to-copy-paste-with-system-clipboard.md │ ├── create-user-command-to-open-init-config.md │ ├── jump-between-changes-in-current-file.md │ ├── run-a-lua-statement-from-the-command-prompt.md │ ├── run-nvim-with-factory-defaults.md │ └── set-up-vim-plug-with-neovim.md ├── netlify/ │ └── override-the-default-yarn-version.md ├── next-auth/ │ └── adjust-the-shape-of-the-user-type.md ├── nextjs/ │ ├── avoid-conflicting-files.md │ ├── create-files-and-directories-for-dynamic-routes.md │ ├── define-url-redirects-in-the-next-config.md │ ├── fetch-does-not-work-in-api-serverless-function.md │ ├── make-environment-variable-publicly-available.md │ ├── match-middleware-on-groups-of-paths.md │ ├── organize-pages-in-route-groups.md │ ├── precedence-of-dot-env-files.md │ ├── push-a-route-with-a-url-object.md │ ├── redirect-an-unauthorized-user.md │ ├── remove-a-query-param-from-the-url.md │ └── ship-public-assets-with-a-nextjs-app.md ├── phoenix/ │ ├── bypass-template-rendering.md │ ├── check-the-installed-version.md │ ├── generate-new-app-without-brunch.md │ ├── render-a-template-to-a-string.md │ ├── serve-static-assets-from-custom-directory.md │ ├── specifying-the-digest-directory.md │ └── specifying-the-server-port.md ├── planetscale/ │ ├── see-what-databases-you-have-access-to.md │ └── seed-production-data-into-another-branch.md ├── pnpm/ │ ├── execute-a-command-from-the-workspace-root.md │ ├── install-command-runs-for-entire-workspace.md │ └── list-the-installed-version-of-a-specific-package.md ├── postgres/ │ ├── a-better-null-display-character.md │ ├── add-foreign-key-constraint-without-a-full-lock.md │ ├── add-on-delete-cascade-to-foreign-key-constraint.md │ ├── add-unique-constraint-using-existing-index.md │ ├── adding-composite-uniqueness-constraints.md │ ├── aggregate-a-column-into-an-array.md │ ├── assumed-radius-of-the-earth.md │ ├── auto-expanded-display.md │ ├── between-symmetric.md │ ├── capitalize-all-the-words.md │ ├── change-the-current-directory-for-psql.md │ ├── change-the-owner-of-a-sequence.md │ ├── check-if-clusters-are-upgrade-compatible.md │ ├── check-if-the-local-server-is-running.md │ ├── check-if-user-role-exists-for-database.md │ ├── check-table-for-any-orphaned-records.md │ ├── check-the-size-of-databases-in-a-cluster.md │ ├── checking-inequality.md │ ├── checking-the-type-of-a-value.md │ ├── clear-the-screen-in-psql-2.md │ ├── clear-the-screen-in-psql.md │ ├── compute-hashes-with-pgcrypto.md │ ├── compute-median-instead-of-average.md │ ├── compute-the-levenshtein-distance-of-two-strings.md │ ├── compute-the-md5-hash-of-a-string.md │ ├── concatenate-strings-with-a-separator.md │ ├── configure-the-timezone.md │ ├── constructing-a-range-of-dates.md │ ├── convert-a-string-to-a-timestamp.md │ ├── count-how-many-records-there-are-of-each-type.md │ ├── count-records-by-type.md │ ├── count-the-number-of-items-in-an-array.md │ ├── count-the-number-of-trues-in-an-aggregate-query.md │ ├── create-a-cluster-in-a-specific-data-directory.md │ ├── create-a-composite-primary-key.md │ ├── create-a-table-from-the-structure-of-another.md │ ├── create-an-index-across-two-columns.md │ ├── create-an-index-without-locking-the-table.md │ ├── create-and-execute-sql-statements-with-gexec.md │ ├── create-database-uses-template1.md │ ├── create-hstore-from-two-arrays.md │ ├── create-table-adds-a-data-type.md │ ├── creating-conditional-constraints.md │ ├── creating-custom-types.md │ ├── day-of-week-by-name-for-a-date.md │ ├── day-of-week-for-a-date.md │ ├── default-schema.md │ ├── defining-arrays.md │ ├── determine-types-of-jsonb-records.md │ ├── determining-the-age-of-things.md │ ├── difference-between-explain-and-explain-analyze.md │ ├── different-ways-to-define-an-interval.md │ ├── dump-all-databases-to-a-sql-file.md │ ├── dump-and-restore-a-database.md │ ├── dump-the-sql-needed-recreate-a-table.md │ ├── duplicate-a-local-database.md │ ├── edit-existing-functions.md │ ├── enable-logging-of-database-activity.md │ ├── enforce-uniqueness-on-column-expression.md │ ├── escaping-a-quote-in-a-string.md │ ├── escaping-string-literals-with-dollar-quoting.md │ ├── export-query-results-to-a-csv.md │ ├── extracting-nested-json-data.md │ ├── fetch-data-from-an-endpoint-in-sql.md │ ├── fetch-specific-number-of-results.md │ ├── find-duplicate-records-in-table-without-unique-id.md │ ├── find-records-that-contain-duplicate-values.md │ ├── find-records-that-have-multiple-associated-records.md │ ├── find-the-data-directory.md │ ├── find-the-location-of-postgres-config-files.md │ ├── fizzbuzz-with-common-table-expressions.md │ ├── force-ssl-when-making-a-psql-connection.md │ ├── generate-a-uuid.md │ ├── generate-modern-primary-key-columns.md │ ├── generate-random-alphanumeric-identifier.md │ ├── generate-random-uuids-without-an-extension.md │ ├── generate-series-of-numbers.md │ ├── generating-uuids-with-pgcrypto.md │ ├── get-a-quick-approximate-count-of-a-table.md │ ├── get-row-count-for-most-recent-query.md │ ├── get-the-size-of-a-database.md │ ├── get-the-size-of-a-table.md │ ├── get-the-size-of-an-index.md │ ├── get-the-size-on-disk-of-an-index.md │ ├── getting-a-slice-of-an-array.md │ ├── group-by-the-result-of-a-function-call.md │ ├── idempotent-inserts.md │ ├── include-all-queries-in-the-log-file.md │ ├── include-columns-in-a-covering-index.md │ ├── include-multiple-tables-in-a-pg-dump.md │ ├── insert-a-bunch-of-records-with-generate-series.md │ ├── insert-just-the-defaults.md │ ├── inspect-progress-of-long-running-create-index.md │ ├── install-postgres-with-uuid-ossp-using-asdf.md │ ├── integers-in-postgres.md │ ├── intervals-of-time-by-week.md │ ├── is-it-null-or-not-null.md │ ├── label-dollar-quoted-strings-with-a-tag.md │ ├── limit-execution-time-of-statements.md │ ├── list-all-columns-of-a-specific-type.md │ ├── list-all-rows-in-a-table.md │ ├── list-all-the-databases.md │ ├── list-all-versions-of-a-function.md │ ├── list-available-schemas.md │ ├── list-connections-to-a-database.md │ ├── list-database-objects-with-disk-usage.md │ ├── list-database-users.md │ ├── list-databases-available-for-connecting.md │ ├── list-various-kinds-of-objects.md │ ├── lower-is-faster-than-ilike.md │ ├── manage-major-versions-with-brew-and-direnv.md │ ├── max-identifier-length-is-63-bytes.md │ ├── open-heroku-database-in-postico-from-terminal.md │ ├── output-explain-query-plan-in-different-formats.md │ ├── pg-prefix-is-reserved-for-system-schemas.md │ ├── postgres-does-not-support-unsigned-integers.md │ ├── prepare-execute-and-deallocate-statements.md │ ├── pretty-print-data-sizes.md │ ├── pretty-printing-jsonb-rows.md │ ├── prevent-a-query-from-running-too-long.md │ ├── print-the-query-buffer-in-psql.md │ ├── put-unique-constraint-on-generated-column.md │ ├── references-target-primary-key-by-default.md │ ├── remove-not-null-constraint-from-a-column.md │ ├── renaming-a-sequence.md │ ├── renaming-a-table.md │ ├── restart-a-sequence.md │ ├── restarting-sequences-when-truncating-tables.md │ ├── salt-and-hash-a-password-with-pgcrypto.md │ ├── send-a-command-to-psql.md │ ├── set-a-seed-for-the-random-number-generator.md │ ├── set-a-statement-timeout-threshold-for-a-session.md │ ├── set-inclusion-with-hstore.md │ ├── set-up-a-project-local-cluster-with-postgres-app.md │ ├── sets-with-the-values-command.md │ ├── shorthand-absolute-value-operator.md │ ├── show-all-versions-of-an-operator.md │ ├── show-reconstructed-constraints-for-a-table.md │ ├── show-the-hidden-queries-behind-backslash-commands.md │ ├── sleeping.md │ ├── special-math-operators.md │ ├── storing-emails-with-citext.md │ ├── string-contains-another-string.md │ ├── survey-of-user-defined-ordering-of-records.md │ ├── switch-non-castable-column-type-with-using-clause.md │ ├── switch-the-running-postgres-server-version.md │ ├── table-names-are-treated-as-lower-case-by-default.md │ ├── temporarily-disable-triggers.md │ ├── temporary-tables.md │ ├── terminating-a-connection.md │ ├── the-nullif-function.md │ ├── timestamp-functions.md │ ├── toggling-the-pager-in-psql.md │ ├── track-psql-history-separately-per-database.md │ ├── trim-leading-and-trailing-space-from-string.md │ ├── truncate-all-rows.md │ ├── truncate-tables-with-dependents.md │ ├── turn-timing-on.md │ ├── two-ways-to-compute-factorial.md │ ├── two-ways-to-escape-a-quote-in-a-string.md │ ├── types-by-category.md │ ├── unable-to-infer-data-type-in-production.md │ ├── union-all-rows-including-duplicates.md │ ├── use-a-psqlrc-file-for-common-settings.md │ ├── use-a-trigger-to-mirror-inserts-to-another-table.md │ ├── use-argument-indexes.md │ ├── use-not-valid-to-immediately-enforce-a-constraint.md │ ├── use-rename-to-hot-swap-two-tables.md │ ├── use-variables-in-an-anonymous-function.md │ ├── using-expressions-in-indexes.md │ ├── using-intervals-to-offset-time.md │ ├── who-is-the-current-user.md │ ├── word-count-for-a-column.md │ └── write-a-query-result-to-file.md ├── prisma/ │ ├── apply-separate-formatting-with-a-blank-line.md │ ├── batch-insert-records-with-create-many.md │ ├── check-if-database-and-schema-are-not-in-sync.md │ ├── configure-client-to-log-sql-queries.md │ ├── execute-a-raw-sql-query.md │ ├── grab-a-limited-set-of-records.md │ ├── open-connections-to-multiple-databases.md │ ├── override-table-name-for-prisma-model.md │ └── specify-alternate-location-for-prisma-schema.md ├── python/ │ ├── access-instance-variables.md │ ├── access-most-recent-return-value-in-repl.md │ ├── break-debugger-on-first-line-of-program.md │ ├── check-if-package-is-installed-with-pip.md │ ├── control-passing-of-time-in-tests.md │ ├── create-a-dummy-dataframe-in-pandas.md │ ├── create-a-range-of-descending-values.md │ ├── dunder-methods.md │ ├── easy-key-value-aggregates-with-defaultdict.md │ ├── install-with-pip-for-specific-interpreter.md │ ├── iterate-first-n-items-from-enumerable.md │ ├── iterate-over-a-dictionary.md │ ├── keep-a-tally-with-collections-counter.md │ ├── load-a-file-into-the-python-repl.md │ ├── look-inside-pytest-tmp-path.md │ ├── override-the-boolean-context-of-a-class.md │ ├── parse-relative-time-to-datetime-object.md │ ├── store-and-access-immutable-data-in-a-tuple.md │ ├── test-a-function-with-pytest.md │ ├── use-pipx-to-install-end-user-apps.md │ └── use-verbose-flag-to-get-more-diff.md ├── rails/ │ ├── access-secrets-in-a-rails-5-2-app.md │ ├── active-record-query-for-this-or-that.md │ ├── add-a-check-constraint-to-a-table.md │ ├── add-a-database-index-if-it-does-not-already-exist.md │ ├── add-a-foreign-key-reference-to-a-table.md │ ├── add-a-generated-column-to-a-postgresql-table.md │ ├── add-a-reference-column-with-an-index.md │ ├── add-activerecord-error-not-tied-to-any-attribute.md │ ├── add-color-to-the-irb-console-prompt.md │ ├── add-react-with-webpacker-to-a-new-rails-app.md │ ├── add-timestamptz-columns-with-the-migration-dsl.md │ ├── adjust-the-production-log-level.md │ ├── advance-the-date.md │ ├── all-or-nothing-database-transactions.md │ ├── allow-associations-to-be-optional.md │ ├── allow-list-params-anywhere-with-strong-params.md │ ├── alphabetize-schema-columns-to-keep-them-consistent.md │ ├── alter-the-rails-setup-script.md │ ├── apply-basic-html-formatting-to-block-of-text.md │ ├── assert-two-arrays-have-the-same-items-with-rspec.md │ ├── attach-a-file-with-capybara.md │ ├── attribute-getter-without-the-recursion.md │ ├── attribute-was.md │ ├── autosave-false-on-activerecord-associations.md │ ├── bind-parameters-to-activerecord-sql-query.md │ ├── build-a-hash-of-model-attributes.md │ ├── capture-development-emails-with-mailhog.md │ ├── capybara-page-status-code.md │ ├── cast-common-boolean-like-values-to-booleans.md │ ├── change-the-nullability-of-a-column.md │ ├── change-the-time-zone-offset-of-a-datetime-object.md │ ├── check-how-database-is-configured.md │ ├── check-if-activerecord-update-fails.md │ ├── check-if-any-records-have-a-null-value.md │ ├── check-specific-attributes-on-activerecord-array.md │ ├── check-the-current-named-log-level.md │ ├── clean-up-memory-hungry-rails-console-processes.md │ ├── code-statistics-for-an-application.md │ ├── columns-with-default-values-are-nil-on-create.md │ ├── comparing-datetimes-down-to-second-precision.md │ ├── conditional-class-selectors-in-haml.md │ ├── convert-a-symbol-to-a-constant.md │ ├── convert-json-field-to-hash-with-indifferent-access.md │ ├── count-the-number-of-records-by-attribute.md │ ├── create-a-custom-named-references-column.md │ ├── create-a-join-table-with-the-migration-dsl.md │ ├── create-table-with-bigint-id-as-primary-key.md │ ├── creating-records-of-has-one-associations.md │ ├── custom-validation-message.md │ ├── customize-paths-and-helpers-for-devise-routes.md │ ├── customize-template-for-new-schema-migration.md │ ├── customize-the-path-of-a-resource-route.md │ ├── define-the-root-path-for-the-app.md │ ├── delete-paranoid-records.md │ ├── demodulize-a-class-name.md │ ├── determine-the-configured-primary-key-type.md │ ├── different-ways-to-add-a-foreign-key-reference.md │ ├── disambiguate-where-in-a-joined-relation.md │ ├── empty-find-by-returns-first-record.md │ ├── enforce-locals-passed-to-a-partial.md │ ├── ensure-a-rake-task-cannot-write-data.md │ ├── ensure-migrations-use-the-latest-schema.md │ ├── ensure-record-saved-with-after-commit-callback.md │ ├── filter-active-model-validation-errors.md │ ├── filter-active-storage-blobs-to-only-images.md │ ├── find-or-create-a-record-with-factory-bot.md │ ├── find-records-with-multiple-associated-records.md │ ├── force-all-users-to-sign-out.md │ ├── format-datetime-with-builtin-formats.md │ ├── format-specific-html-erb-template-files.md │ ├── generate-a-model.md │ ├── generate-a-rails-app-from-the-main-branch.md │ ├── generating-and-executing-sql.md │ ├── get-a-quick-approximate-count-of-a-large-table.md │ ├── get-active-record-attribute-directly-from-database.md │ ├── get-an-array-of-values-from-the-database.md │ ├── get-an-empty-activerecord-relation.md │ ├── get-formatted-utc-offset-value.md │ ├── get-help-with-a-rails-app-update.md │ ├── get-the-column-names-for-a-model.md │ ├── get-the-current-time.md │ ├── grab-a-random-record-from-the-database.md │ ├── handle-named-arguments-in-a-rake-task.md │ ├── hash-slicing.md │ ├── ignore-poltergeist-javascript-errors.md │ ├── include-devise-helpers-in-your-controller-tests.md │ ├── inspect-configuration-of-database-connection.md │ ├── inspect-previous-changes-to-activerecord-object.md │ ├── link-to-the-current-page-with-query-params.md │ ├── list-all-installable-rails-versions.md │ ├── list-the-enqueued-jobs.md │ ├── load-a-file-when-starting-rails-console.md │ ├── load-records-in-batches-with-find-each.md │ ├── log-sql-queries-executed-by-activerecord.md │ ├── look-up-time-zone-info-for-identifier.md │ ├── make-a-string-attribute-easy-to-inquire-about.md │ ├── make-action-mailer-synchronous-in-test.md │ ├── make-remove-column-migration-reversible.md │ ├── manage-timestamps-with-upsert.md │ ├── manually-run-a-migration-from-rails-console.md │ ├── mark-a-migration-as-irreversible.md │ ├── mark-for-destruction.md │ ├── mask-an-activerecord-attribute.md │ ├── merge-a-scope-into-an-activerecord-query.md │ ├── migrating-up-down-up.md │ ├── mock-rails-environment-with-an-inquiry-instance.md │ ├── order-matters-for-rescue-from-blocks.md │ ├── override-text-displayed-by-form-label.md │ ├── parameterize-a-string-with-underscores.md │ ├── params-includes-submission-button-info.md │ ├── params-is-a-hash-with-indifferent-access.md │ ├── parse-query-params-from-a-url.md │ ├── parse-request-params-in-rack-attack-block.md │ ├── perform-sql-explain-with-activerecord.md │ ├── polymorphic-path-helpers.md │ ├── prefer-select-all-over-execute-for-read-queries.md │ ├── pretend-generations.md │ ├── prevent-mailer-previews-from-cluttering-database.md │ ├── prevent-writes-with-a-sandboxed-rails-console.md │ ├── provide-fake-form-helper-to-controllers.md │ ├── query-a-single-value-from-the-database.md │ ├── read-in-environment-specific-config-values.md │ ├── read-only-models.md │ ├── rebuild-tailwind-bundle-for-dev-server.md │ ├── remove-a-database-column-from-a-table.md │ ├── remove-the-default-value-on-a-column.md │ ├── render-an-alternative-action-mailer-template.md │ ├── render-the-response-body-in-controller-specs.md │ ├── replace-an-index-with-a-unique-index.md │ ├── rescue-from-with-a-separate-method.md │ ├── rescue-from.md │ ├── respond-with-json-regardless-of-content-type.md │ ├── restart-puma-server-by-touching-restart-file.md │ ├── retrieve-an-object-if-it-exists.md │ ├── rollback-a-couple-migrations.md │ ├── rollback-a-specific-migration-out-of-order.md │ ├── rounding-numbers-with-precision.md │ ├── run-a-rake-task-programmatically.md │ ├── run-commands-with-specific-rails-version.md │ ├── run-dev-processes-with-overmind-instead-of-foreman.md │ ├── run-rails-console-with-remote-dokku-app.md │ ├── run-some-code-whenever-rails-console-starts.md │ ├── scaffold-auth-functionality-with-rails-8-generator.md │ ├── schedule-sidekiq-jobs-out-into-the-future.md │ ├── scope-records-to-a-lower-or-upper-bound.md │ ├── secure-passwords-with-rails-and-bcrypt.md │ ├── select-a-select-by-selector.md │ ├── select-a-specific-rails-version-to-install.md │ ├── select-value-for-sql-counts.md │ ├── serialize-with-fast-jsonapi-in-a-rails-app.md │ ├── set-a-timestamp-field-to-the-current-time.md │ ├── set-datetime-to-include-time-zone-in-migrations.md │ ├── set-default-as-sql-function-in-migration.md │ ├── set-default-url-options-for-entire-application.md │ ├── set-meta-tags-in-erb-views.md │ ├── set-schema-search-path.md │ ├── set-statement-timeout-for-all-postgres-connections.md │ ├── set-the-default-development-port.md │ ├── show-pending-migrations.md │ ├── show-rails-models-with-pry.md │ ├── show-rails-routes-with-pry.md │ ├── skip-validations-when-creating-a-record.md │ ├── specify-new-attributes-for-find-or-create-by.md │ ├── temporarily-disable-strong-params.md │ ├── temporarily-turn-off-pending-migrations-error.md │ ├── test-for-a-subset-of-attributes-on-a-model.md │ ├── test-if-an-instance-variable-was-assigned.md │ ├── test-if-deliver-later-is-called-for-a-mailer.md │ ├── test-out-url-and-path-helpers-in-the-console.md │ ├── truncate-almost-all-tables.md │ ├── update-column-versus-update-attribute.md │ ├── upgrading-your-manifest-for-sprockets-4.md │ ├── use-irb-and-ruby-flags-with-rails-console.md │ ├── use-ruby-extension-for-template-file.md │ ├── useful-active-support-constants-for-durations.md │ ├── validate-column-data-with-check-constraints.md │ ├── verify-and-read-a-signed-cookie-value.md │ ├── where-am-i-in-the-partial-iteration.md │ ├── why-redirect-and-return-in-controllers.md │ ├── wipe-out-all-precompiled-assets.md │ ├── write-reversible-migration-to-set-default.md │ └── write-safer-where-clauses-with-placeholders.md ├── react/ │ ├── a-component-is-just-a-bag-of-data.md │ ├── access-the-latest-lifecycle-methods-in-an-old-app.md │ ├── accessing-env-vars-in-create-react-app.md │ ├── accessing-location-within-reach-router.md │ ├── allow-md-as-an-extension-with-gatsby-mdx.md │ ├── alter-the-display-name-of-a-component.md │ ├── building-a-react-app-in-the-browser.md │ ├── check-the-type-of-a-child-component.md │ ├── conditionally-including-event-handler-functions.md │ ├── create-a-snowpack-bundled-react-app.md │ ├── create-dynamically-named-custom-react-components.md │ ├── create-react-app-comes-with-lodash.md │ ├── create-react-app-has-a-default-test-setup-file.md │ ├── css-important-is-not-supported-by-inline-styles.md │ ├── debug-jest-tests-in-create-react-app.md │ ├── defining-state-in-a-simple-class-component.md │ ├── destructure-variables-as-props-to-a-component.md │ ├── details-tags-are-a-controllable-component.md │ ├── dispatch-anywhere-with-redux.md │ ├── dynamically-add-props-to-a-child-component.md │ ├── dynamically-create-html-elements.md │ ├── enforce-specific-values-with-proptypes.md │ ├── focus-an-input-with-useref-hook.md │ ├── force-a-component-to-only-have-one-child.md │ ├── forcing-a-child-remount-with-the-key-prop.md │ ├── formik-connected-components.md │ ├── formiks-validation-schema-as-a-function.md │ ├── inactive-and-active-component-styles-with-radium.md │ ├── inline-style-attributes-should-be-camel-cased.md │ ├── manage-state-in-a-functional-component.md │ ├── mapping-over-one-or-many-children.md │ ├── mock-a-function-that-a-component-imports.md │ ├── navigate-with-state-via-reach-router.md │ ├── pairing-a-callback-with-a-usestate-hook.md │ ├── pass-a-function-to-a-usestate-updater.md │ ├── passing-props-down-to-react-router-route.md │ ├── prevent-reach-router-redirect-error-screen-in-dev.md │ ├── proxy-to-an-api-server-in-development-with-cra.md │ ├── quickly-search-for-a-component-with-react-devtools.md │ ├── reach-router-renders-to-a-div.md │ ├── read-only-input-elements.md │ ├── rendering-multiple-nodes-with-fragments.md │ ├── set-the-type-for-a-usestate-hook.md │ ├── specifying-dependencies-of-a-useeffect-hook.md │ ├── spelunking-through-components-with-enzymes-dive.md │ ├── sync-your-react-router-state-with-redux.md │ ├── test-files-in-create-react-app.md │ ├── test-that-element-does-not-render-in-the-component.md │ ├── trigger-effect-only-when-the-component-mounts.md │ ├── update-formik-initial-values-when-props-change.md │ ├── upgrading-to-the-latest-react-in-codesandbox.md │ ├── use-a-ref-to-autofocus-an-input.md │ ├── use-react-16-with-gatsby.md │ ├── use-withrouter-to-pass-down-react-router-history.md │ ├── visually-select-a-react-element-for-inspection.md │ ├── who-is-your-favorite-child.md │ └── wrap-the-root-of-a-gatsby-app-in-a-component.md ├── react-testing-library/ │ ├── check-that-a-component-renders-as-null.md │ ├── find-by-queries-have-async-built-in.md │ ├── pretty-print-some-dom-to-debug-a-test.md │ └── test-a-component-that-uses-react-portals.md ├── react_native/ │ └── avoid-the-notch-with-safeareaview.md ├── reason/ │ ├── break-out-of-a-while-loop.md │ ├── compile-reason-to-native-with-dune.md │ ├── compile-reason-with-an-ocaml-package-using-dune.md │ ├── create-a-map-of-strings.md │ ├── create-a-stream-from-an-array.md │ ├── creating-a-2d-array.md │ ├── data-structures-with-self-referential-types.md │ ├── defining-variants-with-constructor-arguments.md │ ├── dynamically-create-a-printf-string-format.md │ ├── exhaustive-pattern-matching-of-list-variants.md │ ├── format-the-current-file-within-vim.md │ ├── generate-a-native-reasonml-project-with-pesy.md │ ├── generate-starter-reason-projects.md │ ├── helping-the-compiler-help-us-with-variants.md │ ├── inline-component-styles-with-reason-react.md │ ├── is-this-a-directory-or-a-file.md │ ├── making-things-mutable.md │ ├── modifying-a-string-with-blit-string.md │ ├── multi-argument-functions-as-syntactic-sugar.md │ ├── pattern-match-on-exceptions.md │ ├── quickly-bootstrap-a-react-app-using-reason.md │ ├── seeding-and-generating-random-integers.md │ ├── stream-a-file-line-by-line.md │ ├── string-interpolation-with-integers-and-sprintf.md │ ├── string-interpolation-with-quoted-strings.md │ ├── trying-out-reasonml-in-codesandbox.md │ ├── two-ways-to-find-an-item-in-a-list.md │ ├── using-optional-labeled-function-arguments.md │ └── wrapping-a-component-for-use-in-javascript.md ├── remix/ │ ├── get-query-params-from-the-request-url.md │ ├── markdown-and-mdx-files-are-rendered-to-routes.md │ ├── relative-and-absolute-paths-in-links.md │ ├── run-the-development-server-from-another-port.md │ └── set-the-title-of-a-page.md ├── rspec/ │ ├── avoid-accidentally-disabling-pry.md │ ├── check-specific-arguments-to-received-method.md │ ├── configure-tests-to-run-in-random-order.md │ ├── find-minimal-set-of-tests-causing-a-flicker.md │ ├── format-test-results-as-a-json-file.md │ ├── run-tests-with-documentation-formatting.md │ └── use-specific-cache-store-in-a-single-test.md ├── ruby/ │ ├── a-basic-case-statement.md │ ├── a-shorthand-for-rerunning-failed-tests-with-rspec.md │ ├── add-comments-to-regex-with-free-spacing.md │ ├── add-linux-as-a-bundler-platform.md │ ├── add-progress-reporting-to-long-running-script.md │ ├── are-they-all-true.md │ ├── assert-about-an-objects-attributes-with-rspec.md │ ├── assoc-for-hashes.md │ ├── audit-your-ruby-project-for-any-cves.md │ ├── avoid-double-negation-with-minitest-refute.md │ ├── block-comments.md │ ├── block-syntaxes-have-different-precedence.md │ ├── build-http-and-https-urls.md │ ├── chaining-multiple-rspec-change-matchers.md │ ├── check-for-any-overlaps-in-list-of-ranges.md │ ├── check-if-a-url-resolves-to-200.md │ ├── check-if-an-object-includes-a-module.md │ ├── check-return-status-of-running-a-shell-command.md │ ├── clamp-to-an-endless-range.md │ ├── click-on-text-with-capybara.md │ ├── colorful-output-with-minitest.md │ ├── comparing-arrays-in-rspec.md │ ├── comparing-class-hierarchy-relationships.md │ ├── construct-a-constant-from-a-string.md │ ├── convert-a-unix-epoch-timestamp-to-a-time-object.md │ ├── create-a-csv-table-object.md │ ├── create-a-hash-from-an-array-of-arrays.md │ ├── create-a-module-of-utility-functions.md │ ├── create-an-array-of-stringed-numbers.md │ ├── create-listing-of-all-middleman-pages.md │ ├── create-mock-class-that-can-be-overridden.md │ ├── create-named-structs-with-struct-new.md │ ├── create-thumbnail-image-for-a-pdf.md │ ├── decompose-unicode-character-with-diacritic-mark.md │ ├── defaulting-to-frozen-string-literals.md │ ├── define-a-custom-rspec-matcher.md │ ├── define-a-method-on-a-struct.md │ ├── define-multiline-strings-with-heredocs.md │ ├── destructure-the-first-item-from-an-array.md │ ├── destructuring-arrays-in-blocks.md │ ├── disable-interpolation-for-a-heredoc-string.md │ ├── disassemble-some-codes.md │ ├── double-splat-to-merge-hashes.md │ ├── edit-previous-parts-of-the-pry-buffer-history.md │ ├── editing-code-in-pry.md │ ├── encode-a-string-as-url-safe-base64.md │ ├── enumerate-a-pairing-of-every-two-sequential-items.md │ ├── evaluating-one-off-commands.md │ ├── exclude-values-from-an-array.md │ ├── execute-several-commands-with-backtick-heredoc.md │ ├── exit-a-process-with-an-error-message.md │ ├── expect-a-method-to-be-called-and-actually-call-it.md │ ├── extract-a-column-of-data-from-a-csv-file.md │ ├── extract-capture-group-matches-with-string-slices.md │ ├── factory-girl-sequences.md │ ├── fail.md │ ├── fetch-warns-about-superseding-block-argument.md │ ├── filter-by-type.md │ ├── find-the-min-and-max-with-a-single-call.md │ ├── finding-the-source-of-ruby-methods.md │ ├── format-a-hash-into-a-string-template.md │ ├── forward-all-arguments-to-another-method.md │ ├── gather-positional-arguments-in-method-definition.md │ ├── generate-a-signed-jwt-token.md │ ├── generate-ruby-version-and-gemset-files-with-rvm.md │ ├── get-info-about-your-ruby-gems-environment.md │ ├── get-specific-values-from-hashes-and-arrays.md │ ├── get-the-names-of-the-month.md │ ├── get-the-output-of-running-a-system-program.md │ ├── get-utc-offset-for-different-time-zones.md │ ├── identify-outdated-gems.md │ ├── if-you-detect-none.md │ ├── include-extra-context-in-a-honeybadger-notify.md │ ├── ins-and-outs-of-pry.md │ ├── install-and-require-gems-inline-without-gemfile.md │ ├── install-latest-version-of-ruby-with-asdf.md │ ├── invoking-rake-tasks-multiple-times.md │ ├── irb-has-built-in-benchmarking-with-ruby-3.md │ ├── iterate-with-an-offset-index.md │ ├── join-uri-path-parts.md │ ├── jump-out-of-a-nested-context-with-throw-catch.md │ ├── last-raised-exception-in-the-call-stack.md │ ├── limit-split.md │ ├── list-the-running-ruby-version.md │ ├── listing-local-variables.md │ ├── load-a-module-and-execute-a-statement.md │ ├── make-a-long-string-of-text-readable.md │ ├── make-an-executable-ruby-script.md │ ├── make-structs-easier-to-use-with-keyword-initialization.md │ ├── map-with-index-over-an-array.md │ ├── mock-method-chain-calls-with-rspec.md │ ├── mocking-requests-with-partial-uris-using-regex.md │ ├── multi-line-comments.md │ ├── named-regex-captures-are-assigned-to-variables.md │ ├── navigate-back-in-the-browser-with-capybara.md │ ├── next-and-previous-floats.md │ ├── open-struct-has-bad-performance-characteristics.md │ ├── or-operator-precedence.md │ ├── output-bytecode-for-a-ruby-program.md │ ├── override-the-initial-sequence-value.md │ ├── parallel-bundle-install.md │ ├── parse-json-into-an-open-struct.md │ ├── parsing-a-csv-with-quotes-in-the-data.md │ ├── pass-a-block-to-count.md │ ├── passing-arbitrary-methods-as-blocks.md │ ├── passing-arguments-to-a-rake-task.md │ ├── pattern-match-values-from-a-hash.md │ ├── percent-notation.md │ ├── precedence-of-logical-operators.md │ ├── prevent-erb-lint-from-removing-opening-tags.md │ ├── print-data-to-formatted-table.md │ ├── question-mark-operator.md │ ├── rake-only-lists-tasks-with-descriptions.md │ ├── read-the-first-line-from-a-file.md │ ├── refer-to-implicit-block-argument-with-it.md │ ├── reference-hash-key-with-safe-navigation.md │ ├── regenerate-lock-file-with-newer-bundler.md │ ├── rendering-erb.md │ ├── replace-the-current-process-with-an-external-command.md │ ├── require-entire-gemfile-in-pry-session.md │ ├── rerun-only-failures-with-rspec.md │ ├── retry-a-block-after-an-exception.md │ ├── return-the-thing-being-printed.md │ ├── returning-with-sequel.md │ ├── rexml-is-a-bundled-gem-as-of-ruby-3-0-0.md │ ├── run-an-older-version-of-bundler.md │ ├── running-a-single-minitest-example.md │ ├── safe-navigation-operator.md │ ├── scripting-with-rvm.md │ ├── scroll-to-top-of-page-with-capybara.md │ ├── search-for-gem-versions-available-to-install.md │ ├── set-default-tasks-for-rake-to-run.md │ ├── set-rvm-default-ruby.md │ ├── shift-the-month-on-a-date-object.md │ ├── show-public-methods-with-pry.md │ ├── show-the-bundler-location-of-an-installed-gem.md │ ├── silence-the-output-of-a-ruby-statement-in-pry.md │ ├── single-and-double-quoted-string-notation.md │ ├── skip-specific-cves-when-auditing-your-bundle.md │ ├── skip-the-front-of-an-array-with-drop.md │ ├── specify-default-for-data-definition.md │ ├── specify-dependencies-for-a-rake-task.md │ ├── specify-how-random-array-sample-is.md │ ├── split-a-float-into-its-integer-and-decimal.md │ ├── squeeze-out-the-extra-space.md │ ├── stack-heredocs-in-a-method-call.md │ ├── string-interpolation-with-instance-variables.md │ ├── summing-collections.md │ ├── triple-equals-the-case-equality-operator.md │ ├── turn-key-and-values-arrays-into-a-hash.md │ ├── turning-any-class-into-an-enumerator.md │ ├── turning-things-into-hashes.md │ ├── uncaught-exceptions-in-pry.md │ ├── undef-method-and-the-inheritance-hierarchy.md │ ├── uninstall-specific-version-of-a-ruby-gem.md │ ├── unpacking-strings-into-binary.md │ ├── up-and-down-with-integers.md │ ├── update-the-gemfile-bundled-with-version.md │ ├── use-a-case-statement-as-a-cond-statement.md │ ├── use-dotenv-in-a-non-rails-project.md │ ├── use-tap-for-better-test-data-setup.md │ ├── using-bcrypt-to-create-and-check-hashed-passwords.md │ ├── what-to-do-when-you-dont-rescue.md │ ├── who-are-my-ancestors.md │ ├── wrap-things-in-an-array-even-hashes.md │ └── zero-padding.md ├── sed/ │ ├── apply-multiple-substitutions-to-the-input.md │ ├── equivalence-classes-of-repetition-metachars.md │ ├── extract-value-from-command-output-with-sed.md │ ├── grab-all-the-method-names-defined-in-a-ruby-file.md │ ├── grab-the-first-line-of-a-file.md │ ├── osx-sed-does-regex-a-bit-different.md │ ├── output-only-lines-involved-in-a-substitution.md │ ├── reference-a-capture-in-the-regex.md │ ├── reference-the-full-match-in-the-replacement.md │ └── use-an-alternative-delimiter-in-a-substitution.md ├── shell/ │ ├── check-if-the-first-argument-is-given.md │ └── format-and-print-the-current-date-and-time.md ├── sqlite/ │ ├── display-results-in-readable-column-format.md │ └── explore-the-database-schema.md ├── streaming/ │ └── monitor-an-audio-input-device-in-obs.md ├── tailwind/ │ ├── apply-tailwind-classes-to-existing-css-class.md │ ├── base-styles-for-text-link.md │ ├── disable-and-enable-a-button.md │ ├── specify-paths-for-purging-unused-css.md │ └── use-tailwind-typography-prose-in-dark-mode.md ├── taskfile/ │ ├── create-interactive-picker-for-set-of-subtasks.md │ └── run-a-task-if-it-meets-criteria.md ├── tmux/ │ ├── access-past-copy-buffer-history.md │ ├── add-bindings-to-split-panes-to-current-directory.md │ ├── adjusting-window-pane-size.md │ ├── break-current-pane-out-to-separate-window.md │ ├── change-base-directory-of-existing-session.md │ ├── change-base-directory-without-detaching.md │ ├── change-the-default-prefix-key.md │ ├── create-a-named-tmux-session.md │ ├── create-a-new-session-in-a-new-server.md │ ├── cycle-through-layouts.md │ ├── display-titles-for-each-pane-in-a-window.md │ ├── enabling-vi-mode.md │ ├── get-mouse-copy-paste-working-in-kitty.md │ ├── hiding-the-status-bar.md │ ├── jumping-between-sessions.md │ ├── kill-all-your-tmux-sessions.md │ ├── kill-other-connections-to-a-session.md │ ├── kill-the-current-session.md │ ├── list-all-key-bindings.md │ ├── list-processes-running-across-all-sessions.md │ ├── list-sessions.md │ ├── open-new-splits-to-the-current-directory.md │ ├── open-new-window-with-a-specific-directory.md │ ├── organizing-windows.md │ ├── paging-up-and-down.md │ ├── pane-killer.md │ ├── reclaiming-the-entire-window.md │ ├── remove-the-delay-on-the-escape-key.md │ ├── rename-the-current-session.md │ ├── reset-an-option-back-to-its-default-value.md │ ├── set-environment-variables-when-creating-session.md │ ├── set-session-specific-environment-variables.md │ ├── set-up-forwarding-prefix-for-nested-session.md │ ├── show-the-current-value-for-an-option.md │ ├── swap-split-panes.md │ ├── switch-to-a-specific-session-and-window.md │ ├── tmux-in-your-tmux.md │ └── toggle-between-two-common-sessions.md ├── typescript/ │ ├── add-generic-typing-to-an-anonymous-function.md │ ├── add-types-to-an-object-destructuring.md │ ├── compiler-checks-for-unused-params-and-variables.md │ ├── create-a-non-empty-array-type.md │ ├── create-a-union-type-from-an-array.md │ ├── create-union-type-from-constants.md │ ├── extract-object-type-keys-into-a-union-type.md │ ├── extract-object-type-values-into-a-union-type.md │ ├── generate-an-initial-tsconfig-file.md │ ├── generate-inferred-type-from-zod-schema.md │ ├── get-the-return-type-of-an-async-function.md │ ├── ignore-all-errors-in-a-typescript-file.md │ ├── interfaces-with-the-same-name-are-merged.md │ ├── narrow-the-type-of-an-array-to-its-values.md │ ├── re-export-an-imported-type.md │ ├── set-path-alias-for-cleaner-imports.md │ ├── type-narrowing-with-const-vs-let-strings.md │ ├── type-narrowing-with-similarly-shaped-objects.md │ ├── type-promise-results-with-the-awaited-type.md │ ├── use-an-array-check-for-type-narrowing.md │ └── zero-config-environments-for-trying-out-types.md ├── unix/ │ ├── all-the-environment-variables.md │ ├── apply-successive-filters-to-lines-in-less.md │ ├── authorize-a-curl-request.md │ ├── cat-a-file-with-line-numbers.md │ ├── cat-files-with-color-using-bat.md │ ├── change-default-shell-for-a-user.md │ ├── change-to-that-new-directory.md │ ├── check-connected-stripe-account-name.md │ ├── check-if-a-port-is-in-use.md │ ├── check-if-command-is-executable-before-using.md │ ├── check-ssh-key-fingerprints-of-known-hosts.md │ ├── check-the-current-working-directory.md │ ├── check-the-installed-openssl-version.md │ ├── clear-the-screen.md │ ├── combine-all-my-tils-into-a-single-file.md │ ├── command-line-length-limitations.md │ ├── compare-two-variables-in-a-bash-script.md │ ├── configure-cd-to-behave-like-pushd-in-zsh.md │ ├── convert-jpeg-to-png-with-ffmpeg.md │ ├── convert-svg-to-favicon.md │ ├── copying-file-contents-to-system-paste-buffer.md │ ├── copying-nested-directories-with-ditto.md │ ├── count-the-lines-in-a-csv-where-a-column-is-empty.md │ ├── count-the-number-of-matches-in-a-grep.md │ ├── count-the-number-of-ripgrep-pattern-matches.md │ ├── count-the-number-of-words-on-a-webpage.md │ ├── create-a-file-descriptor-with-process-substitution.md │ ├── create-a-filename-with-the-current-date.md │ ├── create-a-sequence-of-values-with-a-step.md │ ├── curl-with-cookies.md │ ├── curling-for-headers.md │ ├── curling-with-basic-auth-credentials.md │ ├── determine-ipv4-and-ipv6-public-ip-addresses.md │ ├── diff-two-files-in-unified-format.md │ ├── different-ways-to-generate-a-v4-uuid.md │ ├── display-all-the-terminal-colors.md │ ├── display-free-disk-space.md │ ├── display-line-numbers-while-using-less.md │ ├── display-the-contents-of-a-directory-as-a-tree.md │ ├── do-a-dry-run-of-an-rsync.md │ ├── do-not-overwrite-existing-files.md │ ├── download-a-file-with-curl.md │ ├── enable-multi-select-of-results-with-fzf.md │ ├── exclude-a-command-from-the-zsh-history-file.md │ ├── exclude-a-directory-with-find.md │ ├── exclude-a-specific-file-from-fd-results.md │ ├── exclude-certain-files-from-an-rsync-run.md │ ├── figure-out-the-week-of-the-year-from-the-terminal.md │ ├── file-type-info-with-file.md │ ├── find-a-file-installed-by-brew.md │ ├── find-all-files-matching-a-name-with-fd.md │ ├── find-all-files-with-a-specific-extension-with-fd.md │ ├── find-all-tool-version-files-containing-postgres.md │ ├── find-and-copy-a-value-from-large-json-output.md │ ├── find-any-dotfiles-that-modify-path-env-var.md │ ├── find-duplicate-lines-in-a-file.md │ ├── find-files-with-fd.md │ ├── find-newer-files.md │ ├── find-occurrences-of-multiple-values-with-ripgrep.md │ ├── find-top-level-directories-matching-a-pattern.md │ ├── fix-previous-command-with-fc.md │ ├── fix-shim-path-after-asdf-upgrade.md │ ├── fix-unlinked-node-binaries-with-asdf.md │ ├── format-and-display-small-amounts-of-columnar-data.md │ ├── forward-multiple-ports-over-ssh.md │ ├── generate-a-saml-key-and-certificate-pair.md │ ├── generate-a-sequence-of-numbered-items.md │ ├── generate-base64-encoding-without-newlines.md │ ├── generate-random-20-character-hex-string.md │ ├── get-a-list-of-locales-on-your-system.md │ ├── get-matching-filenames-as-output-from-grep.md │ ├── get-the-sha256-hash-for-a-file.md │ ├── get-the-unix-timestamp.md │ ├── get-word-count-for-all-files-in-git-repo.md │ ├── global-substitution-on-the-previous-command.md │ ├── globbing-for-all-directories-in-zsh.md │ ├── globbing-for-filenames-in-zsh.md │ ├── gracefully-exit-a-script-with-trap.md │ ├── grep-for-files-with-multiple-matches.md │ ├── grep-for-files-without-a-match.md │ ├── grep-for-multiple-patterns.md │ ├── have-script-shellcheck-itself-when-executing.md │ ├── hexdump-a-compiled-file.md │ ├── ignore-a-directory-during-ripgrep-search.md │ ├── ignore-the-alias-when-running-a-command.md │ ├── include-ignore-files-in-ripgrep-search.md │ ├── inspect-exif-data-for-an-image-file.md │ ├── interactively-browse-availabile-node-versions.md │ ├── interactively-switch-asdf-package-versions.md │ ├── interpret-cron-schedule-from-the-cli.md │ ├── jump-to-the-ends-of-your-shell-history.md │ ├── kill-everything-running-on-a-certain-port.md │ ├── killing-a-frozen-ssh-session.md │ ├── last-argument-of-the-last-command.md │ ├── less-with-style.md │ ├── limit-protocols-used-in-a-curl-command.md │ ├── list-all-fonts-on-your-machine.md │ ├── list-all-the-enabled-zsh-options.md │ ├── list-all-users.md │ ├── list-files-in-a-single-column.md │ ├── list-files-ordered-by-modification-date.md │ ├── list-names-of-files-with-matches.md │ ├── list-of-sessions-to-a-machine.md │ ├── list-parent-pid-with-ps.md │ ├── list-stats-for-a-file.md │ ├── list-the-available-jdks.md │ ├── list-the-pid-and-name-of-current-shell-process.md │ ├── list-the-stack-of-remembered-directories.md │ ├── list-txt-dns-records-for-a-domain.md │ ├── load-env-vars-in-bash-script.md │ ├── look-through-all-files-that-have-been-git-stashed.md │ ├── make-direnv-less-noisy.md │ ├── make-neovim-the-default-way-to-view-man-pages.md │ ├── manually-pass-two-git-files-to-delta.md │ ├── map-a-domain-to-localhost.md │ ├── negative-look-ahead-search-with-ripgrep.md │ ├── occupy-a-local-port-with-netcat.md │ ├── only-show-the-matches.md │ ├── open-the-current-command-in-an-editor.md │ ├── output-the-last-n-bytes-of-a-large-file.md │ ├── partial-string-matching-in-bash-scripts.md │ ├── pid-of-the-current-shell.md │ ├── print-a-range-of-lines-for-a-file-with-bat.md │ ├── print-datetime-represented-by-unix-timestamp.md │ ├── print-milliseconds-in-human-readable-format.md │ ├── print-out-files-in-reverse.md │ ├── print-the-current-date-in-human-readable-format.md │ ├── produce-a-lowercase-v4-uuid.md │ ├── provide-a-fallback-value-for-unset-parameter.md │ ├── remove-a-directory-called-dash-p.md │ ├── rename-a-bunch-of-files-by-constructing-mv-commands.md │ ├── repeat-yourself.md │ ├── replace-pattern-across-many-files-in-a-project.md │ ├── run-a-command-repeatedly-several-times.md │ ├── run-a-curl-command-without-the-progress-meter.md │ ├── safely-edit-the-sudoers-file-with-vim.md │ ├── saying-yes.md │ ├── search-files-specific-to-a-language.md │ ├── search-for-homebrew-packages-to-install.md │ ├── search-history.md │ ├── search-man-page-descriptions.md │ ├── securely-remove-files.md │ ├── see-where-asdf-gets-current-tool-version.md │ ├── set-the-asdf-package-version-for-a-single-shell.md │ ├── shorten-ssh-commands-with-aliases.md │ ├── show-a-file-preview-when-searching-with-fzf.md │ ├── show-disk-usage-for-the-current-directory.md │ ├── show-the-size-of-everything-in-a-directory.md │ ├── show-tree-view-of-processes-and-subprocesses.md │ ├── skip-paging-if-output-fits-on-screen-with-less.md │ ├── sort-in-numerical-order.md │ ├── specify-the-language-for-a-file-with-bat.md │ ├── ssh-escape-sequences.md │ ├── ssh-with-a-specific-key.md │ ├── ssh-with-port-forwarding.md │ ├── switch-versions-of-a-brew-formula.md │ ├── tell-direnv-to-load-the-env-file.md │ ├── touch-access-and-modify-times-individually.md │ ├── transform-text-to-lowercase.md │ ├── type-fewer-paths-with-brace-expansion.md │ ├── undo-changes-made-to-current-terminal-prompt.md │ ├── undo-some-command-line-editing.md │ ├── unrestrict-where-ripgrep-searches.md │ ├── update-package-versions-known-by-asdf-plugin.md │ ├── use-fzf-to-change-directories.md │ ├── use-negative-lookbehind-matching-with-ripgrep.md │ ├── use-regex-pattern-matching-with-grep.md │ ├── view-a-web-page-in-the-terminal.md │ ├── view-the-source-for-a-brew-formula.md │ ├── watch-the-difference.md │ ├── watch-this-run-repeatedly.md │ ├── where-are-the-binaries.md │ ├── xargs-default-command-is-echo.md │ └── xargs-ignores-alias-substitution-by-default.md ├── vercel/ │ ├── add-web-server-layer-redirects.md │ ├── deploy-an-app-without-pushing-an-empty-commit.md │ ├── naming-of-the-vercel-config-file.md │ ├── pin-specific-pnpm-version-for-builds.md │ └── share-development-environment-variables-via-cli.md ├── vim/ │ ├── aborting-git-commits-and-rebases.md │ ├── absolute-and-relative-line-numbers.md │ ├── add-a-file-without-loading-it.md │ ├── add-custom-dictionary-words.md │ ├── all-the-ways-to-write-and-quit-in-vim.md │ ├── almost-the-end-of-the-line.md │ ├── alternate-files-with-vim-rails.md │ ├── always-keep-the-gutter-open.md │ ├── amend-commits-with-fugitive.md │ ├── backspace-options.md │ ├── beginning-and-end-of-previous-change.md │ ├── blank-lines-above-and-below.md │ ├── breaking-the-undo-sequence.md │ ├── buffer-time-travel.md │ ├── build-and-install-a-go-program.md │ ├── bypass-on-save-tooling-when-writing-file.md │ ├── case-aware-substitution-with-vim-abolish.md │ ├── case-insensitive-substitution.md │ ├── center-the-cursor.md │ ├── check-for-an-executable.md │ ├── check-your-current-color-scheme.md │ ├── clear-out-the-jump-list.md │ ├── close-all-other-splits.md │ ├── close-all-other-windows.md │ ├── close-the-current-buffer.md │ ├── coerce-the-current-filetype.md │ ├── coercing-casing-with-vim-abolish.md │ ├── configure-fzf-to-use-fd-for-file-finding.md │ ├── count-the-number-of-matches.md │ ├── create-a-new-directory-in-netrw.md │ ├── create-a-new-file-in-a-new-directory.md │ ├── creating-non-existent-directories.md │ ├── default-netrw-to-tree-liststyle.md │ ├── delete-every-other-line.md │ ├── delete-lines-that-match-a-pattern.md │ ├── delete-to-the-end-of-the-line.md │ ├── deleting-buffers-in-bufexplorer.md │ ├── deleting-directories-of-files-from-netrw.md │ ├── detect-if-you-are-on-a-mac.md │ ├── difference-between-wq-and-x.md │ ├── display-word-count-stats.md │ ├── edges-of-the-selection.md │ ├── edit-a-file-at-a-specific-line-number.md │ ├── edit-a-file-starting-on-the-last-line.md │ ├── end-of-the-word.md │ ├── escaping-terminal-mode-in-an-nvim-terminal.md │ ├── filter-lines-through-an-external-program.md │ ├── find-the-nth-character-position-in-a-file.md │ ├── fix-the-spelling-of-a-word.md │ ├── fold-a-visual-selection-and-expand-it-back.md │ ├── for-when-that-escape-key-is-hard-to-reach.md │ ├── format-long-lines-to-text-width.md │ ├── from-ruby-variables-to-javascript-variables.md │ ├── generate-and-edit-rails-migration.md │ ├── get-the-pid-of-the-session.md │ ├── go-back-to-the-previous-window.md │ ├── go-to-beginning-and-end-of-line.md │ ├── go-to-file-with-line-number.md │ ├── grepping-through-the-vim-help-files.md │ ├── head-of-file-name.md │ ├── help-for-non-normal-mode-features.md │ ├── highlighting-search-matches.md │ ├── horizontal-to-vertical-and-back-again.md │ ├── increment-all-the-numbers.md │ ├── incremental-searching.md │ ├── interact-with-the-alternate-file.md │ ├── interactive-buffer-list.md │ ├── joining-lines-together.md │ ├── jump-back-to-the-latest-jump-position.md │ ├── jump-between-and-stage-git-hunks-with-fugitive.md │ ├── jump-to-matching-pair.md │ ├── jump-to-the-next-misspelling.md │ ├── list-all-buffers.md │ ├── list-autocmds-configured-for-the-current-buffer.md │ ├── list-of-plugins.md │ ├── load-a-directory-of-files-into-the-buffer-list.md │ ├── make-directories-for-the-current-file.md │ ├── marks-across-vim-sessions.md │ ├── match-the-beginning-and-end-of-words.md │ ├── moving-to-a-specific-line.md │ ├── navigate-to-the-nth-column-on-a-line.md │ ├── navigating-by-blank-lines.md │ ├── netrw-listing-styles.md │ ├── next-modified-buffer.md │ ├── normal-mode-binding-to-just-quit.md │ ├── open-a-tag-in-a-split-window.md │ ├── open-an-unnamed-buffer.md │ ├── open-fzf-result-in-a-split.md │ ├── open-routes-file-with-vim-rails.md │ ├── open-the-directory-of-the-current-file.md │ ├── open-the-fugitive-git-summary-window.md │ ├── open-the-gemfile.md │ ├── open-the-latest-rails-migration.md │ ├── open-the-selected-lines-in-github-with-gbrowse.md │ ├── open-vim-to-a-tag-definition.md │ ├── opening-a-url.md │ ├── opening-man-pages-in-vim.md │ ├── paste-a-register-from-insert-mode.md │ ├── preventing-typos-with-abbreviations.md │ ├── previous-buffer.md │ ├── previous-visual-selection.md │ ├── print-the-relative-path-of-the-current-file.md │ ├── print-version-information.md │ ├── quick-file-info.md │ ├── quick-man-pages.md │ ├── quick-quickfix-list-navigation.md │ ├── quickly-fix-a-misspelled-word.md │ ├── quickly-switch-to-a-buffer-by-number.md │ ├── quit-when-there-is-an-argument-list.md │ ├── read-in-the-contents-of-a-rails-file.md │ ├── reindenting-your-code.md │ ├── rename-a-file-through-netrw.md │ ├── rename-current-file.md │ ├── repeat-the-previous-change.md │ ├── repeating-characters.md │ ├── replace-a-character.md │ ├── reset-target-tslime-pane.md │ ├── reverse-a-group-of-lines.md │ ├── reword-a-commit-message-with-fugitive.md │ ├── rotate-everything-by-13-letters.md │ ├── rotate-the-orientation-of-split-windows.md │ ├── running-bundle-with-vim-bundler.md │ ├── scrolling-relative-to-the-cursor.md │ ├── search-backward-through-a-file.md │ ├── searching-for-hex-digits.md │ ├── select-several-results-from-an-fzf-search.md │ ├── set-end-of-line-markers.md │ ├── set-your-color-scheme.md │ ├── setting-filetype-with-modelines.md │ ├── show-all-syntax-highlighting-rules.md │ ├── show-matching-entries-for-help.md │ ├── source-original-vimrc-when-using-neovim.md │ ├── specify-the-line-height-of-the-quick-fix-window.md │ ├── split-different.md │ ├── split-the-current-window.md │ ├── splitting-for-new-files.md │ ├── sum-a-bunch-of-numbers-in-the-current-file.md │ ├── swap-occurrences-of-two-words.md │ ├── swap-the-position-of-two-split-windows.md │ ├── swapping-split-windows.md │ ├── switch-moving-end-of-visual-selection.md │ ├── tabs-to-spaces.md │ ├── the-black-hole-register.md │ ├── the-vim-info-file.md │ ├── toggle-absolute-and-relative-paths-in-bufexplorer.md │ ├── toggling-syntax-highlighting.md │ ├── turning-of-search-highlighting.md │ ├── unloading-a-buffer.md │ ├── use-active-window-with-bufexplorer.md │ ├── use-the-terminal-inside-a-vim-session.md │ ├── using-vim-surround-with-a-visual-selection.md │ ├── verbose-commits-with-fugitive.md │ ├── view-commit-history-of-a-file.md │ ├── view-the-current-file-in-github.md │ ├── viewing-man-pages-with-man-vim.md │ ├── vim-without-the-extras.md │ ├── what-is-on-the-runtime-path.md │ ├── whole-line-auto-completion.md │ └── wrap-with-some-room.md ├── vscode/ │ ├── add-the-vscode-cli-to-your-path.md │ ├── advance-through-search-results.md │ ├── enable-breadcrumbs-for-version-126-release.md │ ├── find-the-location-of-user-settings-json-file.md │ ├── jump-to-problems-in-the-current-file.md │ ├── open-an-integrated-terminal-window.md │ ├── open-file-on-remote-like-github.md │ ├── pop-open-the-quick-fix-window.md │ ├── step-through-project-wide-search-results.md │ ├── synchronize-vim-clipboard-with-system-clipboard.md │ ├── toggle-between-terminals.md │ └── turn-off-display-of-tabs-for-files.md ├── webpack/ │ ├── better-module-imports-with-aliases.md │ ├── debugging-with-full-source-maps.md │ ├── run-eslint-as-a-preloader.md │ ├── specify-port-of-cra-webpack-dev-server.md │ └── use-a-specific-config-file.md ├── workflow/ │ ├── access-1password-credential-from-cli.md │ ├── add-hotkeys-for-specific-raycast-extensions.md │ ├── add-subscriber-to-kit-form-via-api.md │ ├── add-subtitles-to-existing-mux-video-asset.md │ ├── allow-key-repeating-with-cursor.md │ ├── break-justfile-into-separate-hidden-steps.md │ ├── change-window-name-in-iterm.md │ ├── configure-email-redirect-with-cloudflare.md │ ├── control-media-with-drop-keyboard.md │ ├── convert-an-epub-document-to-pdf-on-mac.md │ ├── create-a-local-sanity-dataset-backup.md │ ├── create-a-public-url-for-a-local-server.md │ ├── create-todo-items-in-logseq.md │ ├── do-project-time-tracking-from-the-cli.md │ ├── enable-dev-tools-for-safari.md │ ├── forward-stripe-events-to-local-server.md │ ├── get-url-for-github-user-profile-photo.md │ ├── get-your-public-ip-address.md │ ├── import-a-github-project-into-codesandbox.md │ ├── interactively-kill-a-process-with-fkill.md │ ├── open-slacks-keyboard-shortcuts-reference-panel.md │ ├── pop-videos-out-as-picture-in-picture.md │ ├── prune-the-excess-from-node-modules.md │ ├── rotate-an-image-to-be-oriented-upright.md │ ├── see-overlaps-for-a-set-of-time-zones.md │ ├── send-a-message-to-a-discord-channel.md │ ├── send-a-pdf-to-your-kindle.md │ ├── set-recurring-reminders-in-slack.md │ ├── show-linting-errors-in-zed.md │ ├── temporarily-hide-cleanshot-x-capture-previews.md │ ├── toggle-between-stories-in-storybook.md │ ├── update-asdf-plugins-with-latest-package-versions.md │ ├── view-a-nicely-formatted-csv-in-terminal.md │ └── view-the-pr-for-the-current-github-branch.md ├── xstate/ │ ├── always-use-inline-functions-with-assign.md │ ├── custom-jest-matcher-for-xstate-machine-states.md │ ├── define-event-that-does-internal-self-transition.md │ ├── events-stop-propagating-once-handled.md │ ├── inline-actions-vs-actions-in-machine-options.md │ ├── make-immediate-and-delayed-transitions.md │ ├── simple-states-and-composite-states.md │ ├── start-a-machine-in-a-specific-state.md │ └── use-an-xstate-machine-with-react.md ├── yaml/ │ ├── create-multi-line-strings-without-the-line-breaks.md │ └── yaml-is-a-superset-of-json.md ├── zod/ │ ├── check-if-an-object-is-empty-with-zod.md │ ├── create-a-schema-that-matches-on-any-object.md │ ├── create-union-type-of-nearly-identical-objects.md │ ├── get-readable-errors-from-schema-parse.md │ ├── incorporate-existing-type-into-zod-schema.md │ └── set-custom-error-message-for-nonempty-array.md └── zsh/ ├── a-better-way-to-reload-zsh-configuration.md ├── add-to-the-path-via-path-array.md ├── create-and-jump-into-a-directory.md ├── link-a-scalar-to-an-array.md └── use-a-space-to-exclude-command-from-history.md ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitmodules ================================================ [submodule "notes"] path = notes url = git@github.com:jbranchaud/til-notes-private.git branch = main ignore = all ================================================ FILE: .vimrc ================================================ " vimrc for til " In order for this file to be loaded by Vim, the main `.vimrc` file must " contain `set exrc` and optionally `set secure`. Without those lines, Vim " will ignore this file. function! CountTILs() execute '%s/^- \[//n' endfunction nnoremap c :call CountTILs() augroup DisableMarkdownFormattingForTILReadme autocmd! autocmd BufRead ~/code/til/README.md autocmd! Format augroup END " local til_readme_group = vim.api.nvim_create_augroup('DisableMarkdownFormattingForTILReadme', { clear = true }) " vim.api.nvim_create_autocmd('BufRead', { " command = 'autocmd! Format', " group = til_readme_group, " pattern = vim.fn.expand '~/code/til/README.md', " }) ================================================ FILE: CONTRIBUTING.md ================================================ # Contributing Thanks for wanting to contribute to my TIL repository! I welcome submissions that fix typos and correct wrong or misleading content. All you need to do is fork the repo, make a single commit with a concise, descriptive message, and then submit it as a pull request. If you are interested in contributing your own TIL content, I encourage you to head on over to [til-collective](https://github.com/til-collective/til-collective) and make your submission there. As for this repo, I'd like to maintain it as my own personal learning journey. If you have any questions or suggestions, feel free to open an issue. ================================================ FILE: LICENSE ================================================ Copyright (c) 2015 Josh Branchaud Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README.md ================================================ # TIL > Today I Learned A collection of concise write-ups on small things I learn day to day across a variety of languages and technologies. These are things that don't really warrant a full blog post. These are things I've picked up by [Learning In Public™](https://dev.to/jbranchaud/how-i-built-a-learning-machine-45k9) and working across different projects via [VisualMode](https://www.visualmode.dev/). For a steady stream of TILs, [sign up for my newsletter](https://visualmode.kit.com/newsletter). _1761 TILs and counting..._ See some of the other learning resources I work on: - [Get Started with Vimium](https://egghead.io/courses/get-started-with-vimium~3t5f7) - [Ruby Operator Lookup](https://www.visualmode.dev/ruby-operators) - [Vim Un-Alphabet](https://www.youtube.com/playlist?list=PL46-cKSxMYYCMpzXo6p0Cof8hJInYgohU) If you've learned something here, support my efforts writing daily TILs by [buying me a coffee](https://buymeacoffee.com/jbranchaud) 💜 --- ### Categories * [Ack](#ack) * [Ansible](#ansible) * [Astro](#astro) * [AWS](#aws) * [Bash](#bash) * [Brew](#brew) * [Chrome](#chrome) * [Claude Code](#claude-code) * [Clojure](#clojure) * [CSS](#css) * [Deno](#deno) * [Devops](#devops) * [Docker](#docker) * [Drizzle](#drizzle) * [Elixir](#elixir) * [Gatsby](#gatsby) * [Git](#git) * [GitHub](#github) * [GitHub Actions](#github-actions) * [Go](#go) * [GROQ](#groq) * [Heroku](#heroku) * [HTML](#html) * [HTTP](#http) * [Inngest](#inngest) * [Internet](#internet) * [Java](#java) * [JavaScript](#javascript) * [jj](#jj) * [jq](#jq) * [Kitty](#kitty) * [Linux](#linux) * [LLM](#llm) * [Mac](#mac) * [Mise](#mise) * [MongoDB](#mongodb) * [MySQL](#mysql) * [Neovim](#neovim) * [Netlify](#netlify) * [NextAuth.js](#next-auth) * [Next.js](#nextjs) * [Phoenix](#phoenix) * [Planetscale](#planetscale) * [pnpm](#pnpm) * [PostgreSQL](#postgresql) * [Prisma](#prisma) * [Python](#python) * [Rails](#rails) * [React](#react) * [React Native](#react-native) * [React Testing Library](#react-testing-library) * [ReasonML](#reasonml) * [Remix](#remix) * [RSpec](#rspec) * [Ruby](#ruby) * [sed](#sed) * [Shell](#shell) * [SQLite](#sqlite) * [Streaming](#streaming) * [Tailwind CSS](#tailwind-css) * [Taskfile](#taskfile) * [tmux](#tmux) * [TypeScript](#typescript) * [Unix](#unix) * [Vercel](#vercel) * [Vim](#vim) * [VSCode](#vscode) * [Webpack](#webpack) * [Workflow](#workflow) * [XState](#xstate) * [YAML](#yaml) * [Zod](#zod) * [Zsh](#zsh) --- ### Ack - [ack --bar](ack/ack-bar.md) - [Case-Insensitive Search](ack/case-insensitive-search.md) - [List Available File Types](ack/list-available-file-types.md) ### Ansible - [Loop Over A List Of Dictionaries](ansible/loop-over-a-list-of-dictionaries.md) ### Astro - [Generate Types For A Content Collection](astro/generate-types-for-a-content-collection.md) - [Markdown Files Are Of Type MarkdownInstance](astro/markdown-files-are-of-type-markdown-instance.md) ### AWS - [AWS CLI Requires Groff Executable](aws/aws-cli-requires-groff-executable.md) - [Find And Follow Server Logs](aws/find-and-follow-server-logs.md) - [List RDS Snapshots With Matching Identifier Prefix](aws/list-rds-snapshots-with-matching-identifier-prefix.md) - [Output CLI Results In Different Formats](aws/output-cli-results-in-different-formats.md) - [Sign Up User With Email And Password](aws/sign-up-user-with-email-and-password.md) - [SSH Into An ECS Container](aws/ssh-into-an-ecs-container.md) - [Turn Off Output Pager For A Command](aws/turn-off-output-pager-for-a-command.md) - [Use Specific AWS Profile With CLI](aws/use-specific-aws-profile-with-cli.md) ### Bash - [Edit The Current Command Prompt](bash/edit-the-current-command-prompt.md) ### Brew - [Clean Up Your Brew Installations](brew/clean-up-your-brew-installations.md) - [Configure Brew Environment Variables](brew/configure-brew-environment-variables.md) - [Export List Of Everything Installed By Brew](brew/export-list-of-everything-installed-by-brew.md) - [Install From Nonstandard Brewfile](brew/install-from-nonstandard-brewfile.md) - [Install Go Packages In Brewfile](brew/install-go-packages-in-brewfile.md) - [List All Services Managed By Brew](brew/list-all-services-managed-by-brew.md) ### Chrome - [Access A Value Logged To The Console](chrome/access-a-value-logged-to-the-console.md) - [Chrome Supports Many Unix Keyboard Shortcuts](chrome/chrome-supports-many-unix-keyboard-shortcuts.md) - [Copy Some Data From The Console](chrome/copy-some-data-from-the-console.md) - [Duplicate The Current Tab](chrome/duplicate-the-current-tab.md) - [Easier Access To Network Throttling Controls](chrome/easier-access-to-network-throttling-controls.md) - [Keybinding To Focus The Address Bar](chrome/keybinding-to-focus-the-address-bar.md) - [Open Current Tab In New Window With Vimium](chrome/open-current-tab-in-new-window-with-vimium.md) - [Pause JavaScript From The Source DevTools Panel](chrome/pause-javascript-from-the-source-devtools-panel.md) - [Navigate The Browser History With Vimium](chrome/navigate-the-browser-history-with-vimium.md) - [Pretty Print Tabular Data](chrome/pretty-print-tabular-data.md) - [Reference The Selected Node](chrome/reference-the-selected-node.md) - [Search Tabs With The Vimium Vomnibar](chrome/search-tabs-with-the-vimium-vomnibar.md) - [Selecting DOM Elements Faster Than Ever](chrome/selecting-dom-elements-faster-than-ever.md) - [Simulating Various Connection Speeds](chrome/simulating-various-connection-speeds.md) - [Toggle Device Mode](chrome/toggle-device-mode.md) - [Toggle Open The Console Drawer](chrome/toggle-open-the-console-drawer.md) - [Trigger Commands From The Devtools Command Palette](chrome/trigger-commands-from-the-devtools-command-palette.md) - [View Network Traffic For New Tabs](chrome/view-network-traffic-for-new-tabs.md) ### Claude Code - [Allow Edits From The Start](claude-code/allow-edits-from-the-start.md) - [Monitor Usage Limits From CLI](claude-code/monitor-usage-limits-from-cli.md) - [Open Current Prompt In Default Editor](claude-code/open-current-prompt-in-default-editor.md) - [Resume Specific Session](claude-code/resume-specific-session.md) ### Clojure - [Aggregation Using merge-with](clojure/aggregation-using-merge-with.md) - [Argument Requirements For A Function](clojure/argument-requirements-for-a-function.md) - [Combinations Of Items From A Sequence](clojure/combinations-of-items-from-a-sequence.md) - [Define Something Only Once](clojure/define-something-only-once.md) - [Evaluate One Liners With lein-exec](clojure/evaluate-one-liners-with-lein-exec.md) - [Expanding Macros](clojure/expanding-macros.md) - [Get The Value Of An Environment Variable](clojure/get-the-value-of-an-environment-variable.md) - [List Functions For A Namespace](clojure/list-functions-for-a-namespace.md) - [Load A File Into The REPL](clojure/load-a-file-into-the-repl.md) - [Mapping With An Index](clojure/mapping-with-an-index.md) - [Open JavaDocs](clojure/open-javadocs.md) - [Pretty Print The Last Thing](clojure/pretty-print-the-last-thing.md) - [Quick Clojure Docs](clojure/quick-clojure-docs.md) - [Reductions](clojure/reductions.md) - [Set Max Heap Size](clojure/set-max-heap-size.md) - [Specify the Directory of a Shell Command](clojure/specify-the-directory-of-a-shell-command.md) - [Splitting On Whitespace](clojure/splitting-on-whitespace.md) - [Swap Two Items in a Vector](clojure/swap-two-items-in-a-vector.md) - [Try A Clojure Project In The REPL](clojure/try-a-clojure-project-in-the-repl.md) - [Type of Anything](clojure/type-of-anything.md) - [When Overflow Is Desired](clojure/when-overflow-is-desired.md) ### CSS - [Add Fab Icons To Your Site With FontAwesome 5](css/add-fab-icons-to-your-site-with-fontawesome-5.md) - [Add Line Numbers To A Code Block With Counter](css/add-line-numbers-to-a-code-block-with-counter.md) - [Animate Smoothly Between Two Background Colors](css/animate-smoothly-between-two-background-colors.md) - [Apply Multiple Box Shadows To Single Element](css/apply-multiple-box-shadows-to-single-element.md) - [Apply Styles Based On Dark-Mode Preferences](css/apply-styles-based-on-dark-mode-preferences.md) - [Apply Styles To The Last Child Of A Specific Type](css/apply-styles-to-the-last-child-of-a-specific-type.md) - [Change The Orientation Of An Image](css/change-the-orientation-of-an-image.md) - [Circular Icons With A Massive Border Radius](css/circular-icons-with-a-massive-border-radius.md) - [Clean Up Repetition With :is() Pseudo-Class](css/clean-up-repetition-with-is-pseudo-class.md) - [Conditional Styling For Unsupported CSS Features](css/conditional-styling-for-unsupported-css-features.md) - [Create A Pulsing Background With CSS Animation](css/create-a-pulsing-background-with-css-animation.md) - [Define CSS Custom Properties With CSS Variables](css/define-css-custom-properties-with-scss-variables.md) - [Define HSL Colors With Alpha Values](css/define-hsl-colors-with-alpha-values.md) - [Display Responsive iframe Maintaining Aspect Ratio](css/display-responsive-iframe-maintaining-aspect-ratio.md) - [Dry Up SCSS With Mixins](css/dry-up-scss-with-mixins.md) - [Filter Blur Requires Expensive Calculation](css/filter-blur-requires-expensive-calculation.md) - [Give Elements The Same Width With Flexbox](css/give-elements-the-same-width-with-flexbox.md) - [Let Pointer Events Pass Through An Element](css/let-pointer-events-pass-through-an-element.md) - [Lighten And Darken With CSS Brightness Filter](css/lighten-and-darken-with-css-brightness-filter.md) - [Lighten And Darken With SCSS](css/lighten-and-darken-with-scss.md) - [Make A Block Of Text Respect New Lines](css/make-a-block-of-text-respect-new-lines.md) - [Parameterized SCSS Mixins](css/parameterized-scss-mixins.md) - [Prevent Invisible Elements From Being Clicked](css/prevent-invisible-elements-from-being-clicked.md) - [:root Has Higher Specificity Than html](css/root-has-higher-specificity-than-html.md) - [Style A Background With A Linear Gradient](css/style-a-background-with-a-linear-gradient.md) - [Using Maps In SCSS](css/using-maps-in-scss.md) ### Cursor - [Allow Cursor To Be Launched From CLI](cursor/allow-cursor-to-be-launched-from-cli.md) ### Deno - [Read In The Contents Of A File](deno/read-in-the-contents-of-a-file.md) ### Devops - [Aliasing An Ansible Host](devops/aliasing-an-ansible-host.md) - [Allow Cross-Origin Requests To Include Cookies](devops/allow-cross-origin-requests-to-include-cookies.md) - [Allow HTTPS Through Your UFW Firewall](devops/allow-https-through-your-ufw-firewall.md) - [Check For Cached Site Assocation File For iOS](devops/check-for-cached-site-association-file-for-ios.md) - [Check The Status of All Services](devops/check-the-status-of-all-services.md) - [Check The Syntax Of nginx Files](devops/check-the-syntax-of-nginx-files.md) - [Connect To An RDS PostgreSQL Database](devops/connect-to-an-rds-postgresql-database.md) - [Default Rails Deploy Script On Hatchbox](devops/default-rails-deploy-script-on-hatchbox.md) - [Determine The IP Address Of A Domain](devops/determine-the-ip-address-of-a-domain.md) - [Hatchbox Exports Env Vars With asdf](devops/hatchbox-exports-env-vars-with-asdf.md) - [Path Of The Packets](devops/path-of-the-packets.md) - [Push Non-master Branch To Heroku](devops/push-non-master-branch-to-heroku.md) - [Reload The nginx Configuration](devops/reload-the-nginx-configuration.md) - [Resolve The Public IP Of A URL](devops/resolve-the-public-ip-of-a-url.md) - [Running Out Of inode Space](devops/running-out-of-inode-space.md) - [Set, Get, And Unset Env Vars With Dokku](devops/set-get-and-unset-env-vars-with-dokku.md) - [Set Up Domain For Hatchbox Rails App](devops/set-up-domain-for-hatchbox-rails-app.md) - [SSH Into A Docker Container](devops/ssh-into-a-docker-container.md) - [SSL Certificates Can Cover Multiple Domains](devops/ssl-certificates-can-cover-multiple-domains.md) - [Wipe A Heroku Postgres Database](devops/wipe-a-heroku-postgres-database.md) ### Docker - [Check Postgres Version Running In Docker Container](docker/check-postgres-version-running-in-docker-container.md) - [Configure Different Host And Container Ports](docker/configure-different-host-and-container-ports.md) - [List Running Docker Containers](docker/list-running-docker-containers.md) - [Prevent Containers From Running On Startup](docker/prevent-containers-from-running-on-startup.md) - [Run A Basic PostgreSQL Server In Docker](docker/run-a-basic-postgresql-server-in-docker.md) - [Run SQL Script Against Postgres Container](docker/run-sql-script-against-postgres-container.md) ### Drizzle - [Create bigint Identity Column For Primary Key](drizzle/create-bigint-identity-column-for-primary-key.md) - [Drizzle Tracks Migrations In A Log Table](drizzle/drizzle-tracks-migrations-in-a-log-table.md) - [Get Fields For Inserted Row](drizzle/get-fields-for-inserted-row.md) ### Elixir - [All Values For A Key In A Keyword List](elixir/all-values-for-a-key-in-a-keyword-list.md) - [Append To A Keyword List](elixir/append-to-a-keyword-list.md) - [Assert An Exception Is Raised](elixir/assert-an-exception-is-raised.md) - [Binary Representation Of A String](elixir/binary-representation-of-a-string.md) - [Check For A Substring Match](elixir/check-for-a-substring-match.md) - [Check List Membership](elixir/check-list-membership.md) - [Comparing DateTime Structs](elixir/comparing-datetime-structs.md) - [Compute Intermediate Values In A With Construct](elixir/compute-intermediate-values-in-a-with-construct.md) - [Compute md5 Digest Of A String](elixir/compute-md5-digest-of-a-string.md) - [Counting Records With Ecto](elixir/counting-records-with-ecto.md) - [Create A Date With The Date Sigil](elixir/create-a-date-with-the-date-sigil.md) - [Create A List Of Atoms](elixir/create-a-list-of-atoms.md) - [Creating A PID](elixir/creating-a-pid.md) - [Creating Indexes With Ecto](elixir/creating-indexes-with-ecto.md) - [Defining Multiple Clauses In An Anonymous Function](elixir/defining-multiple-clauses-in-an-anonymous-function.md) - [Determine The Latest Release Of A Hex Package](elixir/determine-the-latest-release-of-a-hex-package.md) - [Do You Have The Time?](elixir/do-you-have-the-time.md) - [Do You Have The Time? - Part 2](elixir/do-you-have-the-time-part-2.md) - [Documentation Lookup With Vim And Alchemist](elixir/documentation-lookup-with-vim-and-alchemist.md) - [Dynamically Generating Atoms](elixir/dynamically-generating-atoms.md) - [Execute Raw SQL In An Ecto Migration](elixir/execute-raw-sql-in-an-ecto-migration.md) - [Expose Internal Representation](elixir/expose-internal-representation.md) - [Include Captures With String.split](elixir/include-captures-with-string-split.md) - [Inspecting The Process Message Queue](elixir/inspecting-the-process-message-queue.md) - [List Functions For A Module](elixir/list-functions-for-a-module.md) - [Listing Files In IEx](elixir/listing-files-in-iex.md) - [Match On A Map In A With Construct](elixir/match-on-a-map-in-a-with-construct.md) - [Passing Around And Using Modules](elixir/passing-around-and-using-modules.md) - [Pattern Matching In Anonymous Functions](elixir/pattern-matching-in-anonymous-functions.md) - [Pipe Into A Case Statement](elixir/pipe-into-a-case-statement.md) - [Quitting IEx](elixir/quitting-iex.md) - [Range Into List Using Comprehensions](elixir/range-into-list-using-comprehensions.md) - [Refer To A Module Within Itself](elixir/refer-to-a-module-within-itself.md) - [Referencing Values In IEx's History](elixir/referencing-values-in-iexs-history.md) - [Remove One List From Another](elixir/remove-one-list-from-another.md) - [Replace Duplicates In A Keyword List](elixir/replace-duplicates-in-a-keyword-list.md) - [Requiring Keys For Structs](elixir/requiring-keys-for-structs.md) - [Reversing A List](elixir/reversing-a-list.md) - [Reversing A List - Part 2](elixir/reversing-a-list-part-2.md) - [Root Directory Of A Project](elixir/root-directory-of-a-project.md) - [Round Floats To Integers](elixir/round-floats-to-integers.md) - [Run ExUnit Tests In A Deterministic Order](elixir/run-exunit-tests-in-a-deterministic-order.md) - [Run The Test At A Specific Line Number](elixir/run-the-test-at-a-specific-line-number.md) - [Same Functions Should Be Grouped Together](elixir/same-functions-should-be-grouped-together.md) - [Skip A Specific Test](elixir/skip-a-specific-test.md) - [String Interpolation With Just About Anything](elixir/string-interpolation-with-just-about-anything.md) - [Unique Indexes With Ecto](elixir/unique-indexes-with-ecto.md) - [Updating Values In A Map](elixir/updating-values-in-a-map.md) - [Using When Clauses In A With Construct](elixir/using-when-clauses-in-a-with-construct.md) - [Virtual Fields With Ecto Schemas](elixir/virtual-fields-with-ecto-schemas.md) - [When Things Don't Match The With Statements](elixir/when-things-dont-match-the-with-statements.md) - [Word Lists For Atoms](elixir/word-lists-for-atoms.md) ### Gatsby - [Add JavaScript To Body Of The Document](gatsby/add-javascript-to-body-of-the-document.md) ### Git - [Accessing a Lost Commit](git/accessing-a-lost-commit.md) - [Add A Range Of Filename To gitignore](git/add-a-range-of-filenames-to-gitignore.md) - [Add Only Tracked Files From A Directory](git/add-only-tracked-files-from-a-directory.md) - [Amend Author Of Previous Commit](git/amend-author-of-previous-commit.md) - [Auto-Squash Those Fixup Commits](git/auto-squash-those-fixup-commits.md) - [Better Diffs With Delta](git/better-diffs-with-delta.md) - [Caching Credentials](git/caching-credentials.md) - [Change The Start Point Of A Branch](git/change-the-start-point-of-a-branch.md) - [Check How A File Is Being Ignored](git/check-how-a-file-is-being-ignored.md) - [Check If A File Has Changed In A Script](git/check-if-a-file-has-changed-in-a-script.md) - [Check If A File Is Under Version Control](git/check-if-a-file-is-under-version-control.md) - [Checking Commit Ancestry](git/checking-commit-ancestry.md) - [Checkout Old Version Of A File](git/checkout-old-version-of-a-file.md) - [Checkout Previous Branch](git/checkout-previous-branch.md) - [Cherry Pick A Range Of Commits](git/cherry-pick-a-range-of-commits.md) - [Cherry Pick Multiple Commits At Once](git/cherry-pick-multiple-commits-at-once.md) - [Clean Out All Local Branches](git/clean-out-all-local-branches.md) - [Clean Out Working Copy With Patched Restore](git/clean-out-working-copy-with-patched-restore.md) - [Clean Up Old Remote Tracking References](git/clean-up-old-remote-tracking-references.md) - [Clear Entries From Git Stash](git/clear-entries-from-git-stash.md) - [Clone A Repo Just For The Files, Without History](git/clone-a-repo-just-for-the-files-without-history.md) - [Clone A Repo Locally From .git](git/clone-a-repo-locally-from-git.md) - [Configure Global gitignore File](git/configure-global-gitignore-file.md) - [Configuring The Pager](git/configuring-the-pager.md) - [Copy A File From Another Branch](git/copy-a-file-from-another-branch.md) - [Count All Files Of Specific Type Tracked By Git](git/count-all-files-of-specific-type-tracked-by-git.md) - [Count Number Of Commits On A Branch](git/count-number-of-commits-on-a-branch.md) - [Create A New Branch With Git Switch](git/create-a-new-branch-with-git-switch.md) - [Delete All Untracked Files](git/delete-all-untracked-files.md) - [Determine Absolute Path Of Top-Level Project Directory](git/determine-absolute-path-of-top-level-project-directory.md) - [Determine The Hash Id For A Blob](git/determine-the-hash-id-for-a-blob.md) - [Diffing With Patience](git/diffing-with-patience.md) - [Dropping Commits With Git Rebase](git/dropping-commits-with-git-rebase.md) - [Dry Runs in Git](git/dry-runs-in-git.md) - [Exclude A File From A Diff Output](git/exclude-a-file-from-a-diff-output.md) - [Exclude A Directory During A Command](git/exclude-a-directory-during-a-command.md) - [Excluding Files Locally](git/excluding-files-locally.md) - [Extend Git With Custom Commands](git/extend-git-with-custom-commands.md) - [Files With Local Changes Cannot Be Removed](git/files-with-local-changes-cannot-be-removed.md) - [Find And Remove Files That Match A Name](git/find-and-remove-files-that-match-a-name.md) - [Find The Date That A File Was Added To The Repo](git/find-the-date-that-a-file-was-added-to-the-repo.md) - [Find The Initial Commit](git/find-the-initial-commit.md) - [Fix Whitespace Errors Throughout Branch Commits](git/fix-whitespace-errors-throughout-branch-commits.md) - [Get Latest Commit Timestamp For A File](git/get-latest-commit-timestamp-for-a-file.md) - [Get The Name Of The Current Branch](git/get-the-name-of-the-current-branch.md) - [Get The Short Version Of The Latest Commit](git/get-the-short-version-of-the-latest-commit.md) - [Grab A Single File From A Stash](git/grab-a-single-file-from-a-stash.md) - [Grep For A Pattern On Another Branch](git/grep-for-a-pattern-on-another-branch.md) - [Grep Over Commit Messages](git/grep-over-commit-messages.md) - [Highlight Extra Whitespace In Diff Output](git/highlight-extra-whitespace-in-diff-output.md) - [Highlight Small Change On Single Line](git/highlight-small-change-on-single-line.md) - [Ignore Changes To A Tracked File](git/ignore-changes-to-a-tracked-file.md) - [Ignore Files Specific To Your Workflow](git/ignore-files-specific-to-your-workflow.md) - [Include A Message With Your Stashed Changes](git/include-a-message-with-your-stashed-changes.md) - [Include Or Exclude Remaining Patch Changes](git/include-or-exclude-remaining-patch-changes.md) - [Include Some Stats In Your Git Log](git/include-some-stats-in-your-git-log.md) - [Intent To Add](git/intent-to-add.md) - [Interactively Checkout Specific Files From A Stash](git/interactively-checkout-specific-files-from-a-stash.md) - [Interactively Unstage Changes](git/interactively-unstage-changes.md) - [Keep File Locally With `git rm`](git/keep-file-locally-with-git-rm.md) - [Last Commit A File Appeared In](git/last-commit-a-file-appeared-in.md) - [List All Files Added During Span Of Time](git/list-all-files-added-during-span-of-time.md) - [List All Files Changed Between Two Branches](git/list-all-files-changed-between-two-branches.md) - [List All Git Aliases From gitconfig](git/list-all-git-aliases-from-gitconfig.md) - [List Branches That Contain A Commit](git/list-branches-that-contain-a-commit.md) - [List Commits On A Branch](git/list-commits-on-a-branch.md) - [List Different Commits Between Two Branches](git/list-different-commits-between-two-branches.md) - [List Filenames Without The Diffs](git/list-filenames-without-the-diffs.md) - [List Just The Files Involved In A Commit](git/list-just-the-files-involved-in-a-commit.md) - [List Most Git Commands](git/list-most-git-commands.md) - [List Untracked Files](git/list-untracked-files.md) - [List Untracked Files For Scripting](git/list-untracked-files-for-scripting.md) - [Move The Latest Commit To A New Branch](git/move-the-latest-commit-to-a-new-branch.md) - [Override The Global Git Ignore File](git/override-the-global-git-ignore-file.md) - [Pick Specific Changes To Stash](git/pick-specific-changes-to-stash.md) - [Pulling In Changes During An Interactive Rebase](git/pulling-in-changes-during-an-interactive-rebase.md) - [Push To A Branch On Another Remote](git/push-to-a-branch-on-another-remote.md) - [Quicker Commit Fixes With The Fixup Flag](git/quicker-commit-fixes-with-the-fixup-flag.md) - [Rebase Commits With An Arbitrary Command](git/rebase-commits-with-an-arbitrary-command.md) - [Reference A Commit Via Commit Message Pattern Matching](git/reference-a-commit-via-commit-message-pattern-matching.md) - [Reference Commits Earlier Than Reflog Remembers](git/reference-commits-earlier-than-reflog-remembers.md) - [Remove Untracked Files From A Directory](git/remove-untracked-files-from-a-directory.md) - [Rename A Remote](git/rename-a-remote.md) - [Renaming A Branch](git/renaming-a-branch.md) - [Resetting A Reset](git/resetting-a-reset.md) - [Resolve A Merge Conflict From Stash Pop](git/resolve-a-merge-conflict-from-stash-pop.md) - [Restore File From One Branch To The Current](git/restore-file-from-one-branch-to-the-current.md) - [Review Commits From Before A Certain Date](git/review-commits-from-before-a-certain-date.md) - [Run A Git Command From Outside The Repo](git/run-a-git-command-from-outside-the-repo.md) - [Set A Custom Pager For A Specific Command](git/set-a-custom-pager-for-a-specific-command.md) - [Set Default Branch Name For New Repos](git/set-default-branch-name-for-new-repos.md) - [Set Up GPG Signing Key](git/set-up-gpg-signing-key.md) - [Shorthand To Force Push A Branch](git/shorthand-to-force-push-a-branch.md) - [Show All Commits For A File Beyond Renaming](git/show-all-commits-for-a-file-beyond-renaming.md) - [Show Changes For Files That Match A Pattern](git/show-changes-for-files-that-match-a-pattern.md) - [Show Changes In The Compose Commit Message View](git/show-changes-in-the-compose-commit-message-view.md) - [Show File Diffs When Viewing Git Log](git/show-file-diffs-when-viewing-git-log.md) - [Show List Of Most Recently Committed Branches](git/show-list-of-most-recently-committed-branches.md) - [Show Only Commits That Touch Specific Lines](git/show-only-commits-that-touch-specific-lines.md) - [Show Summary Stats For Current Branch](git/show-summary-stats-for-current-branch.md) - [Show The diffstat Summary Of A Commit](git/show-the-diffstat-summary-of-a-commit.md) - [Show The Good And The Bad With Git Bisect](git/show-the-good-and-the-bad-with-git-bisect.md) - [Show What Is In A Stash](git/show-what-is-in-a-stash.md) - [Single Key Presses in Interactive Mode](git/single-key-presses-in-interactive-mode.md) - [Skip A Bad Commit When Bisecting](git/skip-a-bad-commit-when-bisecting.md) - [Skip Git Hooks As Needed](git/skip-git-hooks-as-needed.md) - [Skip Pre-Commit Hooks](git/skip-pre-commit-hooks.md) - [Staging Changes Within Vim](git/staging-changes-within-vim.md) - [Staging Stashes Interactively](git/staging-stashes-interactively.md) - [Stash A Single Untracked File](git/stash-a-single-untracked-file.md) - [Stash Everything](git/stash-everything.md) - [Stashing Only Unstaged Changes](git/stashing-only-unstaged-changes.md) - [Stashing Untracked Files](git/stashing-untracked-files.md) - [Switch To A Recent Branch With FZF](git/switch-to-a-recent-branch-with-fzf.md) - [Transition A Branch From One Base To Another](git/transition-a-branch-from-one-base-to-another.md) - [Turn Off The Output Pager For One Command](git/turn-off-the-output-pager-for-one-command.md) - [Two Kinds Of Dotted Range Notation](git/two-kinds-of-dotted-range-notation.md) - [Undo Latest Changes Committed To Specific File](git/undo-latest-changes-committed-to-specific-file.md) - [Unstage Changes Wih Git Restore](git/unstage-changes-with-git-restore.md) - [Untrack A Directory Of Files Without Deleting](git/untrack-a-directory-of-files-without-deleting.md) - [Untrack A File Without Deleting It](git/untrack-a-file-without-deleting-it.md) - [Update The URL Of A Remote](git/update-the-url-of-a-remote.md) - [Use External Diff Tool Like Difftastic](git/use-external-diff-tool-like-difftastic.md) - [Using Commands With A Relative Date Format](git/using-commands-with-a-relative-date-format.md) - [Verbose Commit Message](git/verbose-commit-message.md) - [Viewing A File On Another Branch](git/viewing-a-file-on-another-branch.md) - [What Changed?](git/what-changed.md) - [What Is The Current Branch?](git/what-is-the-current-branch.md) - [Whitespace Warnings](git/whitespace-warnings.md) ### GitHub - [Access Your GitHub Profile Photo](github/access-your-github-profile-photo.md) - [Open A PR To An Unforked Repo](github/open-a-pr-to-an-unforked-repo.md) - [Target Another Repo When Creating A PR](github/target-another-repo-when-creating-a-pr.md) - [Tell gh What The Default Repo Is](github/tell-gh-what-the-default-repo-is.md) ### GitHub Actions - [Cache Playwright Dependencies Across Workflows](github-actions/cache-playwright-dependencies-across-workflows.md) - [Capture An Output Value For Use In A Later Step](github-actions/capture-an-output-value-for-use-in-a-later-step.md) - [Disable A Workflow With The gh CLI](github-actions/disable-a-workflow-with-the-gh-cli.md) - [Reference An Encrypted Secret In An Action](github-actions/reference-an-encrypted-secret-in-an-action.md) - [Trigger A Workflow Via An API Call](github-actions/trigger-a-workflow-via-an-api-call.md) - [Use Labels To Block PR Merge](github-actions/use-labels-to-block-pr-merge.md) ### Go - [Access Go Docs Offline](go/access-go-docs-offline.md) - [Add A Method To A Struct](go/add-a-method-to-a-struct.md) - [Basic Delve Debugging Session](go/basic-delve-debugging-session.md) - [Build For A Specific OS And Architecture](go/build-for-a-specific-os-and-architecture.md) - [Check If Cobra Flag Was Set](go/check-if-cobra-flag-was-set.md) - [Combine Two Slices](go/combine-two-slices.md) - [Configure Max String Print Length For Delve](go/configure-max-string-print-length-for-delve.md) - [Connect To A SQLite Database](go/connect-to-a-sqlite-database.md) - [Create A Slice From An Array](go/create-a-slice-from-an-array.md) - [Detect If Stdin Comes From A Redirect](go/detect-if-stdin-comes-from-a-redirect.md) - [Deterministically Seed A Random Number Generator](go/deterministically-seed-a-random-number-generator.md) - [Difference Between Slice And Pointer To Slice](go/difference-between-slice-and-pointer-to-slice.md) - [Do Something N Times](go/do-something-n-times.md) - [Find Executables Installed By Go](go/find-executables-installed-by-go.md) - [Format Date And Time With Time Constants](go/format-date-and-time-with-time-constants.md) - [Not So Random](go/not-so-random.md) - [Parse A String Into Individual Fields](go/parse-a-string-into-individual-fields.md) - [Parse Flags From CLI Arguments](go/parse-flags-from-cli-arguments.md) - [Pass A Struct To A Function](go/pass-a-struct-to-a-function.md) - [Produce The Zero Value Of A Generic Type](go/produce-the-zero-value-of-a-generic-type.md) - [Redirect File To Stdin During Delve Debug](go/redirect-file-to-stdin-during-delve-debug.md) - [Replace The Current Process With An External Command](go/replace-the-current-process-with-an-external-command.md) - [Sleep For A Duration](go/sleep-for-a-duration.md) - [Sort Slice In Ascending Or Descending Order](go/sort-slice-in-ascending-or-descending-order.md) - [Upgrading From An Older Version On Mac](go/upgrading-from-an-older-version-on-mac.md) - [Write A Custom Scan Function For File IO](go/write-a-custom-scan-function-for-file-io.md) ### GROQ - [Grab Multiple Values From A Reference](groq/grab-multiple-values-from-a-reference.md) - [Grab Values From An Array Of References](groq/grab-values-from-an-array-of-references.md) - [Include Attributes When Conditional Check Passes](groq/include-attributes-when-conditional-check-passes.md) - [Include Type Of Operation In Webhook Response](groq/include-type-of-operation-in-webhook-response.md) ### Heroku - [Check Ruby Version For Production App](heroku/check-ruby-version-for-production-app.md) - [Connect To A Database By Color](heroku/connect-to-a-database-by-color.md) - [Deploy A Review App To A Different Stack](heroku/deploy-a-review-app-to-a-different-stack.md) - [Diagnose Problems In A Heroku Postgres Database](heroku/diagnose-problems-in-a-heroku-postgres-database.md) - [Open Dashboard For Specific Add-On](heroku/open-dashboard-for-specific-add-on.md) - [Run SQL Against Remote Postgres Database](heroku/run-sql-against-remote-postgres-database.md) - [Set And Show Heroku Env Variables](heroku/set-and-show-heroku-env-variables.md) - [Specify Default Team And App For Project](heroku/specify-default-team-and-app-for-project.md) - [SSH Into Heroku Server Hosting App](heroku/ssh-into-heroku-server-hosting-app.md) ### HTML - [Adding Alt Text To An Image](html/adding-alt-text-to-an-image.md) - [Allow Number Input To Accept Decimal Values](html/allow-number-input-to-accept-decimal-values.md) - [Determine Which Button Submitted The Form](html/determine-which-button-submitted-the-form.md) - [Disable Auto-Completion For A Form Input](html/disable-auto-completion-for-a-form-input.md) - [Disclose Additional Details](html/disclose-additional-details.md) - [Make Elements Non-Interactive With Inert](html/make-elements-non-interactive-with-inert.md) - [Prevent Search Engines From Indexing A Page](html/prevent-search-engines-from-indexing-a-page.md) - [Render Text As Superscript](html/render-text-as-superscript.md) - [Submit A Form With A Button Outside The Form](html/submit-a-form-with-a-button-outside-the-form.md) ### HTTP - [What Counts As Cross-Origin With CORS?](http/what-counts-as-cross-origin-with-cors.md) ### Inngest - [Ensure Lookup Can Be Retried](inngest/ensure-lookup-can-be-retried.md) - [Exit Function Early Without Retries](inngest/exit-function-early-without-retries.md) ### Internet - [Add Emoji To GitHub Repository Description](internet/add-emoji-to-github-repository-description.md) - [Add Styled Alerts To GitHub Markdown Documents](internet/add-styled-alerts-to-github-markdown-documents.md) - [Analyze Your Website Performance](internet/analyze-your-website-performance.md) - [Check Your Public IP Address](internet/check-your-public-ip-address.md) - [Digraph Unicode Characters Have A Titlecase](internet/digraph-unicode-characters-have-a-titlecase.md) - [Download A Google Doc As Specific Format](internet/download-a-google-doc-as-specific-format.md) - [Enable Keyboard Shortcuts In Gmail](internet/enable-keyboard-shortcuts-in-gmail.md) - [Exclude AI Overview From Google Search](internet/exclude-ai-overview-from-google-search.md) - [Exclude Whitespace Changes From GitHub Diffs](internet/exclude-whitespace-changes-from-github-diffs.md) - [Figure Out Your Public IP Address](internet/figure-out-your-public-ip-address.md) - [Focus The URL Bar](internet/focus-the-url-bar.md) - [Get Random Images From Unsplash](internet/get-random-images-from-unsplash.md) - [Grab The RSS Feed For A Substack Blog](internet/grab-the-rss-feed-for-a-substack-blog.md) - [Hide Overflowing Text For Google Sheets Column](internet/hide-overflowing-text-for-google-sheets-column.md) - [Search Tweets By Author](internet/search-tweets-by-author.md) - [Show All Pivotal Stories With Blockers](internet/show-all-pivotal-stories-with-blockers.md) - [Verify Site Ownership With DNS Record](internet/verify-site-ownership-with-dns-record.md) ### Java - [Ensure Resources Always Get Closed](java/ensure-resources-always-get-closed.md) - [Install Java On Mac With Brew](java/install-java-on-mac-with-brew.md) - [Run A Hello World Program In Eclipse](java/run-a-hello-world-program-in-eclipse.md) ### JavaScript - [Accessing Arguments To A Function](javascript/accessing-arguments-to-a-function.md) - [Add Item To An Array Of References In Sanity](javascript/add-item-to-an-array-of-references-in-sanity.md) - [Basic Date Formatting Without A Library](javascript/basic-date-formatting-without-a-library.md) - [Character Codes from Keyboard Listeners](javascript/character-codes-from-keyboard-listeners.md) - [Check Classes On A DOM Element](javascript/check-classes-on-a-dom-element.md) - [Check If A Number Is Positive Or Negative](javascript/check-if-a-number-is-positive-or-negative.md) - [Check If File Exists Before Reading It](javascript/check-if-file-exists-before-reading-it.md) - [Check If Something Is An Array](javascript/check-if-something-is-an-array.md) - [Check Media Queries From JavaScript](javascript/check-media-queries-from-javascript.md) - [Check The Password Confirmation With Yup](javascript/check-the-password-confirmation-with-yup.md) - [Compare The Equality Of Two Date Objects](javascript/compare-the-equality-of-two-date-objects.md) - [Computed Property Names In ES6](javascript/computed-property-names-in-es6.md) - [Conditionally Include Pairs In An Object](javascript/conditionally-include-pairs-in-an-object.md) - [Configure Jest To Run A Test Setup File](javascript/configure-jest-to-run-a-test-setup-file.md) - [Convert Seconds To Date Object](javascript/convert-seconds-to-date-object.md) - [Create A Cancelable Promise With PCancelable](javascript/create-a-cancelable-promise-with-pcancelable.md) - [Create An Array Containing 1 To N](javascript/create-an-array-containing-1-to-n.md) - [Create An Object With No Properties](javascript/create-an-object-with-no-properties.md) - [Create Bootstrapped Apps With Yarn](javascript/create-bootstrapped-apps-with-yarn.md) - [Create Future And Past Dates From Today](javascript/create-future-and-past-dates-from-today.md) - [Custom Type Checking Error Messages With Yup](javascript/custom-type-checking-error-messages-with-yup.md) - [Default And Named Exports From The Same Module](javascript/default-and-named-exports-from-the-same-module.md) - [Define A Custom Jest Matcher](javascript/define-a-custom-jest-matcher.md) - [Destructure With Access To Nested Value And Parent Value](javascript/destructure-with-access-to-nested-value-and-parent-value.md); - [Destructuring The Rest Of An Array](javascript/destructuring-the-rest-of-an-array.md) - [Enable ES7 Transforms With react-rails](javascript/enable-es7-transforms-with-react-rails.md) - [Ensure Shell Can Find Global npm Binaries](javascript/ensure-shell-can-find-global-npm-binaries.md) - [Easy Date Comparison With DayJS](javascript/easy-date-comparison-with-dayjs.md) - [Expand Emojis With The Spread Operator](javascript/expand-emojis-with-the-spread-operator.md) - [Fill An Input With A Ton Of Text](javascript/fill-an-input-with-a-ton-of-text.md) - [Find The Version Of An Installed Dependency](javascript/find-the-version-of-an-installed-dependency.md) - [Find Where Yarn Is Installing Binaries](javascript/find-where-yarn-is-installing-binaries.md) - [for...in Iterates Over Object Properties](javascript/for-in-iterates-over-object-properties.md) - [Format A Decimal To A Fixed Number Of Digits](javascript/format-a-decimal-to-a-fixed-number-of-digits.md) - [Format A List Of Items By Locale](javascript/format-a-list-of-items-by-locale.md) - [Format Time Zone Identifier](javascript/format-time-zone-identifier.md) - [Formatting Values With Units For Display](javascript/formatting-values-with-units-for-display.md) - [Freeze An Object, Sorta](javascript/freeze-an-object-sorta.md) - [Generate A V4 UUID In The Browser](javascript/generate-a-v4-uuid-in-the-browser.md) - [Generate Random Integers](javascript/generate-random-integers.md) - [Get The Location And Size Of An Element](javascript/get-the-location-and-size-of-an-element.md) - [Get The Response Status From An Axios Error](javascript/get-the-response-status-from-an-axios-error.md) - [Get The Time Components Of A Date](javascript/get-the-time-components-of-a-date.md) - [Get The Time Zone Of The Client Computer](javascript/get-the-time-zone-of-the-client-computer.md) - [Globally Install A Package With Yarn](javascript/globally-install-a-package-with-yarn.md) - [Globally Install Specific Version Of PNPM](javascript/globally-install-specific-version-of-pnpm.md) - [Immutable Remove With The Spread Operator](javascript/immutable-remove-with-the-spread-operator.md) - [Initialize A New JavaScript Project With Yarn](javascript/initialize-a-new-javascript-project-with-yarn.md) - [Install The Latest Version Of Node With Nvm](javascript/install-the-latest-version-of-node-with-nvm.md) - [Interpolate A String Into A Regex](javascript/interpolate-a-string-into-a-regex.md) - [ISO-8601 Formatted Dates Are Interpreted As UTC](javascript/iso-8601-formatted-dates-are-interpreted-as-utc.md) - [Link A JavaScript Package Locally](javascript/link-a-javascript-package-locally.md) - [List Top-Level NPM Dependencies](javascript/list-top-level-npm-dependencies.md) - [Load And Use Env Var In Node Script](javascript/load-and-use-env-var-in-node-script.md) - [Make The Browser Editable With Design Mode](javascript/make-the-browser-editable-with-design-mode.md) - [Make Truly Deep Clone With Structured Clone](javascript/make-truly-deep-clone-with-structured-clone.md) - [Matching A Computed Property In Function Args](javascript/matching-a-computed-property-in-function-args.md) - [Matching Multiple Values In A Switch Statement](javascript/matching-multiple-values-in-a-switch-statement.md) - [Mock A Function With Return Values Using Jest](javascript/mock-a-function-with-return-values-using-jest.md) - [New Dates Can Take Out Of Bounds Values](javascript/new-dates-can-take-out-of-bounds-values.md) - [Numbers Are Empty](javascript/numbers-are-empty.md) - [Object Initialization With Shorthand Property Names](javascript/object-initialization-with-shorthand-property-names.md) - [Obtain Undefined Value With The Void Operator](javascript/obtain-undefined-value-with-the-void-operator.md) - [Open Global npm Config File](javascript/open-global-npm-config-file.md) - [Parse A Date From A Timestamp](javascript/parse-a-date-from-a-timestamp.md) - [Pre And Post Hooks For Yarn Scripts](javascript/pre-and-post-hooks-for-yarn-scripts.md) - [Prevent Hidden Element From Flickering On Load](javascript/prevent-hidden-element-from-flickering-on-load.md) - [Purge Null And Undefined Values From Object](javascript/purge-null-and-undefined-values-from-object.md) - [Random Cannot Be Seeded](javascript/random-cannot-be-seeded.md) - [Reach Into An Object For Nested Data With Get](javascript/reach-into-an-object-for-nested-data-with-get.md) - [Render An Array Of Elements With React 16](javascript/render-an-array-of-elements-with-react-16.md) - [Resolve And Pass Multiple Values From A Then](javascript/resolve-and-pass-multiple-values-from-a-then.md) - [Run A Bash Script From A Node Script](javascript/run-a-bash-script-from-a-node-script.md) - [Run Multiple Node Scripts Concurrently](javascript/run-multiple-node-scripts-concurrently.md) - [Running ES6 Specs With Mocha](javascript/running-es6-specs-with-mocha.md) - [Scoping Variables With A Block Statement](javascript/scoping-variables-with-a-block-statement.md) - [Short Circuit Concurrently When Process Fails](javascript/short-circuit-concurrently-when-process-fails.md) - [Show Description Of All npm Config Options](javascript/show-description-of-all-npm-config-options.md) - [Sleep For A Bit In Async Code](javascript/sleep-for-a-bit-in-async-code.md) - [Sorting Arrays Of Objects With Lodash](javascript/sorting-arrays-of-objects-with-lodash.md) - [Splat Arguments To A Function](javascript/splat-arguments-to-a-function.md) - [Spread Merging Objects Includes Nil Values](javascript/spread-merging-objects-includes-nil-values.md) - [Spread The Rest With ES6](javascript/spread-the-rest-with-es6.md) - [Start Node Process In Specific Timezone](javascript/start-node-process-in-specific-timezone.md) - [String Interpolation With Template Literals](javascript/string-interpolation-with-template-literals.md) - [Support Nested Matching In Custom Jest Matchers](javascript/support-nested-matching-in-custom-jest-matchers.md) - [Tell Jest To Focus On Running Only One Test](javascript/tell-jest-to-focus-on-running-only-one-test.md) - [Tell Node To Treat JS Files As ESM](javascript/tell-node-to-treat-js-files-as-esm.md) - [Tell Prettier To Not Format A Statement](javascript/tell-prettier-to-not-format-a-statement.md) - [Test Coverage Stats With Jest](javascript/test-coverage-stats-with-jest.md) - [Test Timing-Based Code With Jest Fake Timers](javascript/test-timing-based-code-with-jest-fake-timers.md) - [The Comma Operator](javascript/the-comma-operator.md) - [Throttling A Function Call](javascript/throttling-a-function-call.md) - [Timing Processes](javascript/timing-processes.md) - [Transforming ES6 and JSX With Babel 6](javascript/transforming-es6-and-jsx-with-babel-6.md) - [Truthiness of Integer Arrays](javascript/truthiness-of-integer-arrays.md) - [Turn An HTMLCollection Into An Array](javascript/turn-an-html-collection-into-an-array.md) - [Turn Off Console Error Messages In A Test](javascript/turn-off-console-error-messages-in-a-test.md) - [Turn Off npm Funding Message](javascript/turn-off-npm-funding-message.md) - [Waiting On Multiple Promises](javascript/waiting-on-multiple-promises.md) - [Who Am I: NPM Edition](javascript/who-am-i-npm-edition.md) - [Write A JavaScript Object To A JSON File](javascript/write-a-javascript-object-to-a-json-file.md) - [Yarn Commands Without The Emojis](javascript/yarn-commands-without-the-emojis.md) - [Yup Schemas Are Validated Asynchronously](javascript/yup-schemas-are-validated-asynchronously.md) ### jj - [Colocate jj And git Directories For Project](jj/colocate-jj-and-git-directories-for-project.md) - [Describe Current Changes And Create New Change](jj/describe-current-changes-and-create-new-change.md) - [Find System-wide Config File For User](jj/find-system-wide-config-file-for-user.md) - [Squash Changes Into Parent Commit Interactively](jj/squash-changes-into-parent-commit-interactively.md) ### jq - [Combine An Array Of Objects Into A Single Object](jq/combine-an-array-of-objects-into-a-single-object.md) - [Count Each Collection In A JSON Object](jq/count-each-collection-in-a-json-object.md) - [Count The Number Of Things In A JSON File](jq/count-the-number-of-things-in-a-json-file.md) - [Extract A List Of Values](jq/extract-a-list-of-values.md) - [Filter Out Results Based On List Of Values](jq/filter-out-results-based-on-list-of-values.md) - [Find All Objects In An Array Where Key Is Set](jq/find-all-objects-in-an-array-where-key-is-set.md) - [Find All Objects With A Matching Key Value Pair](jq/find-all-objects-with-a-matching-key-value-pair.md) - [Get A Slice Of The Ends Of An Array](jq/get-a-slice-of-the-ends-of-an-array.md) - [Get The First Item For Every Top-Level Key](jq/get-the-first-item-for-every-top-level-key.md) - [Get The Last Item From An Array](jq/get-the-last-item-from-an-array.md) - [Reduce Object To Just Entries Of A Specific Type](jq/reduce-object-to-just-entries-of-a-specific-type.md) - [Turn A List From A Command Into JSON](jq/turn-a-list-from-a-command-into-json.md) - [Zip Two JSON Files Together Based On Shared ID](jq/zip-two-json-files-together-based-on-shared-id.md) ### Kitty - [Set The Title Of A Window](kitty/set-the-title-of-a-window.md) - [Use The Built-In Emoji Picker](kitty/use-the-built-in-emoji-picker.md) ### Linux - [Check Ubuntu Version](linux/check-ubuntu-version.md) - [Configure Your Server Timezone](linux/configure-your-server-timezone.md) - [List The Statuses Of All Upstart Jobs](linux/list-the-statuses-of-all-upstart-jobs.md) - [Show Current System Time And Settings](linux/show-current-system-time-and-settings.md) - [Show Used And Available System Memory](linux/show-used-and-available-system-memory.md) - [Upgrading Ubuntu](linux/upgrading-ubuntu.md) ### LLM - [Send cURL To Claude Text Completion API](llm/send-curl-to-claude-text-completion-api.md) - [Use The llm CLI With Claude Models](llm/use-the-llm-cli-with-claude-models.md) ### Mac - [Access All Screen And Video Capture Options](mac/access-all-screen-and-video-capture-options.md) - [Access System Information On OS X](mac/access-system-information-on-osx.md) - [Access Unsupported Screen Resolutions With RDM](mac/access-unsupported-screen-resolutions-with-rdm.md) - [Add A Bunch Of CLI Utilities With coreutils](mac/add-a-bunch-of-cli-utilities-with-coreutils.md) - [Capture Screenshot To Clipboard From CLI](mac/capture-screenshot-to-clipboard-from-cli.md) - [Check Network Quality Stats From The Command Line](mac/check-network-quality-stats-from-the-command-line.md) - [Clean Up Old Homebrew Files](mac/clean-up-old-homebrew-files.md) - [Control Which Monitor App Switcher Appears On](mac/control-which-monitor-app-switcher-appears-on.md) - [Convert An HEIC Image File To JPG](mac/convert-an-heic-image-file-to-jpg.md) - [Default Screenshot Location](mac/default-screenshot-location.md) - [Detect How Long A User Has Been Idle](mac/detect-how-long-a-user-has-been-idle.md) - [Disable Swipe Navigation For A Specific App](mac/disable-swipe-navigation-for-a-specific-app.md) - [Display A Message With Alfred](mac/display-a-message-with-alfred.md) - [Find The Process Using A Specific Port](mac/find-the-process-using-a-specific-port.md) - [Gesture For Viewing All Windows Of Current App](mac/gesture-for-viewing-all-windows-of-current-app.md) - [Insert A Non-Breaking Space Character](mac/insert-a-non-breaking-space-character.md) - [Inspect Assertions Preventing Sleep](mac/inspect-assertions-preventing-sleep.md) - [Keyboard Shortcuts For Interesting With Text Areas](mac/keyboard-shortcuts-for-interacting-with-text-areas.md) - [Launch Some Confetti](mac/launch-some-confetti.md) - [List All The Say Voices](mac/list-all-the-say-voices.md) - [Open Finder.app To Specific Directory](mac/open-finder-app-to-specific-directory.md) - [Prevent Sleep With The Caffeinate Command](mac/prevent-sleep-with-the-caffeinate-command.md) - [Quickly Type En Dashes And Em Dashes](mac/quickly-type-en-dashes-and-em-dashes.md) - [Require Additional JS Libraries In Postman](mac/require-additional-js-libraries-in-postman.md) - [Resize App Windows With AppleScript](mac/resize-app-windows-with-applescript.md) - [Resizing Both Corners Of A Window](mac/resizing-both-corners-of-a-window.md) - [Reveal Location Of File In Finder.app](mac/reveal-location-of-file-in-finder-app.md) - [Run A Hardware Check](mac/run-a-hardware-check.md) - [Run AppleScript Commands Inline In The Terminal](mac/run-applescript-commands-inline-in-the-terminal.md) - [Set A Window To Its Default Zoom Level](mac/set-a-window-to-its-default-zoom-level.md) - [Specify App When Opening From Command Line](mac/specify-app-when-opening-from-command-line.md) - [Start Amphetamine Session With AppleScript](mac/start-amphetamine-session-with-applescript.md) - [Uninstall LogiTech G Hub From Mac](mac/uninstall-logitech-g-hub-from-mac.md) - [Use A Different Font With iTerm2](mac/use-a-different-font-with-iterm2.md) - [Use Default Screenshot Shortcuts With CleanShot X](mac/use-default-screenshot-shortcuts-with-cleanshot-x.md) - [View All Windows Of The Current App](mac/view-all-windows-of-the-current-app.md) - [Write System Clipboard To A File](mac/write-system-clipboard-to-a-file.md) ### Mise - [Create Umbrella Task For All Test Tasks](mise/create-umbrella-task-for-all-test-tasks.md) - [List The Files Being Loaded By Mise](mise/list-the-files-being-loaded-by-mise.md) - [Look In Ruby Version Dotfile](mise/look-in-ruby-version-dotfile.md) - [Override Your Project Mise File](mise/override-your-project-mise-file.md) - [Pick From Tasks Using Interactive Picker](mise/pick-from-tasks-using-interactive-picker.md) - [Preserve Color Output For Task Command](mise/preserve-color-output-for-task-command.md) - [Read Existing Dot Env File Into Env Vars](mise/read-existing-dot-env-file-into-env-vars.md) - [Run A Command With Specific Tool Version](mise/run-a-command-with-specific-tool-version.md) - [Search Through Bin Paths For Tool Locations](mise/search-through-bin-paths-for-tool-locations.md) ### MongoDB - [Determine The Database Version](mongodb/determine-the-database-version.md) - [Dump A Remote Database](mongodb/dump-a-remote-database.md) - [Dump And Restore With A Single gzip File](mongodb/dump-and-restore-with-a-single-gzip-file.md) - [Get Size Stats For A Collection](mongodb/get-size-stats-for-a-collection.md) - [List Size Stats For All Collections](mongodb/list-size-stats-for-all-collections.md) ### MySQL - [Change Existing Column To Not Null](mysql/change-existing-column-to-not-null.md) - [Connect To A Database In Safe Update Mode](mysql/connect-to-a-database-in-safe-update-mode.md) - [Default Username And Password For New Instance](mysql/default-username-and-password-for-new-instance.md) - [Display Output In A Vertical Format](mysql/display-output-in-a-vertical-format.md) - [Doing Date Math](mysql/doing-date-math.md) - [Dump A Database To A File](mysql/dump-a-database-to-a-file.md) - [Echo A Message From A SQL File](mysql/echo-a-message-from-a-sql-file.md) - [Get Idea Of What Is In A JSON Column](mysql/get-idea-of-what-is-in-a-json-column.md) - [Ignore Duplicates When Inserting Records](mysql/ignore-duplicates-when-inserting-records.md) - [List Databases And Tables](mysql/list-databases-and-tables.md) - [Run Statements In A Transaction](mysql/run-statements-in-a-transaction.md) - [Select Rows After An Offset](mysql/select-rows-after-an-offset.md) - [Set Value On Null JSON Column](mysql/set-value-on-null-json-column.md) - [Show Create Statement For A Table](mysql/show-create-statement-for-a-table.md) - [Show Tables That Match A Pattern](mysql/show-tables-that-match-a-pattern.md) - [Show Indexes For A Table](mysql/show-indexes-for-a-table.md) ### Neovim - [Allow Neovim To Copy/Paste With System Clipboard](neovim/allow-neovim-to-copy-paste-with-system-clipboard.md) - [Create User Command To Open Init Config](neovim/create-user-command-to-open-init-config.md) - [Jump Between Changes In Current File](neovim/jump-between-changes-in-current-file.md) - [Run A Lua Statement From The Command Prompt](neovim/run-a-lua-statement-from-the-command-prompt.md) - [Run nvim With Factory Defaults](neovim/run-nvim-with-factory-defaults.md) - [Set Up Vim-Plug With Neovim](neovim/set-up-vim-plug-with-neovim.md) ### Netlify - [Override The Default Yarn Version](netlify/override-the-default-yarn-version.md) ### NextAuth.js - [Adjust The Shape Of The User Type](next-auth/adjust-the-shape-of-the-user-type.md) ### Next.js - [Avoid Conflicting Files](nextjs/avoid-conflicting-files.md) - [Create Files And Directories For Dynamic Routes](nextjs/create-files-and-directories-for-dynamic-routes.md) - [Define URL Redirects In The Next Config](nextjs/define-url-redirects-in-the-next-config.md) - [Fetch Does Not Work In API Serverless Function](nextjs/fetch-does-not-work-in-api-serverless-function.md) - [Make Environment Variable Publicly Available](nextjs/make-environment-variable-publicly-available.md) - [Match Middleware On Groups Of Paths](nextjs/match-middleware-on-groups-of-paths.md) - [Organize Pages In Route Groups](nextjs/organize-pages-in-route-groups.md) - [Precedence Of Dot Env Files](nextjs/precedence-of-dot-env-files.md) - [Push A Route With A URL Object](nextjs/push-a-route-with-a-url-object.md) - [Redirect An Unauthorized User](nextjs/redirect-an-unauthorized-user.md) - [Remove A Query Param From The URL](nextjs/remove-a-query-param-from-the-url.md) - [Ship Public Assets With A Next.js App](nextjs/ship-public-assets-with-a-nextjs-app.md) ### Phoenix - [Bypass Template Rendering](phoenix/bypass-template-rendering.md) - [Check The Installed Version](phoenix/check-the-installed-version.md) - [Generate New App Without Brunch](phoenix/generate-new-app-without-brunch.md) - [Render A Template To A String](phoenix/render-a-template-to-a-string.md) - [Serve Static Assets From Custom Directory](phoenix/serve-static-assets-from-custom-directory.md) - [Specifying The Digest Directory](phoenix/specifying-the-digest-directory.md) - [Specifying The Server Port](phoenix/specifying-the-server-port.md) ### Planetscale - [See What Databases You Have Access To](planetscale/see-what-databases-you-have-access-to.md) - [Seed Production Data Into Another Branch](planetscale/seed-production-data-into-another-branch.md) ### pnpm - [Execute A Command From The Workspace Root](pnpm/execute-a-command-from-the-workspace-root.md) - [Install Command Runs For Entire Workspace](pnpm/install-command-runs-for-entire-workspace.md) - [List The Installed Version Of A Specific Package](pnpm/list-the-installed-version-of-a-specific-package.md) ### PostgreSQL - [A Better Null Display Character](postgres/a-better-null-display-character.md) - [Add Foreign Key Constraint Without A Full Lock](postgres/add-foreign-key-constraint-without-a-full-lock.md) - [Add ON DELETE CASCADE To Foreign Key Constraint](postgres/add-on-delete-cascade-to-foreign-key-constraint.md) - [Add Unique Constraint Using Existing Index](postgres/add-unique-constraint-using-existing-index.md) - [Adding Composite Uniqueness Constraints](postgres/adding-composite-uniqueness-constraints.md) - [Aggregate A Column Into An Array](postgres/aggregate-a-column-into-an-array.md) - [Assumed Radius Of The Earth](postgres/assumed-radius-of-the-earth.md) - [Auto Expanded Display](postgres/auto-expanded-display.md) - [Between Symmetric](postgres/between-symmetric.md) - [Capitalize All The Words](postgres/capitalize-all-the-words.md) - [Change The Current Directory For psql](postgres/change-the-current-directory-for-psql.md) - [Change The Owner Of A Sequence](postgres/change-the-owner-of-a-sequence.md) - [Check If Clusters Are Upgrade Compatible](postgres/check-if-clusters-are-upgrade-compatible.md) - [Check If The Local Server Is Running](postgres/check-if-the-local-server-is-running.md) - [Check If User Role Exists For Database](postgres/check-if-user-role-exists-for-database.md) - [Check Table For Any Oprhaned Records](postgres/check-table-for-any-orphaned-records.md) - [Check The Size Of Databases In A Cluster](postgres/check-the-size-of-databases-in-a-cluster.md) - [Checking Inequality](postgres/checking-inequality.md) - [Checking The Type Of A Value](postgres/checking-the-type-of-a-value.md) - [Clear The Screen In psql](postgres/clear-the-screen-in-psql.md) - [Clear The Screen In psql (2)](postgres/clear-the-screen-in-psql-2.md) - [Compute Hashes With pgcrypto](postgres/compute-hashes-with-pgcrypto.md) - [Compute Median Instead Of Average](postgres/compute-median-instead-of-average.md) - [Compute The Levenshtein Distance Of Two Strings](postgres/compute-the-levenshtein-distance-of-two-strings.md) - [Compute The md5 Hash Of A String](postgres/compute-the-md5-hash-of-a-string.md) - [Concatenate Strings With A Separator](postgres/concatenate-strings-with-a-separator.md) - [Configure The Timezone](postgres/configure-the-timezone.md) - [Constructing A Range Of Dates](postgres/constructing-a-range-of-dates.md) - [Convert A String To A Timestamp](postgres/convert-a-string-to-a-timestamp.md) - [Count How Many Records There Are Of Each Type](postgres/count-how-many-records-there-are-of-each-type.md) - [Count Records By Type](postgres/count-records-by-type.md) - [Count The Number Of Items In An Array](postgres/count-the-number-of-items-in-an-array.md) - [Count The Number Of Trues In An Aggregate Query](postgres/count-the-number-of-trues-in-an-aggregate-query.md) - [Create A Cluster In A Specific Data Directory](postgres/create-a-cluster-in-a-specific-data-directory.md) - [Create A Composite Primary Key](postgres/create-a-composite-primary-key.md) - [Create A Table From The Structure Of Another](postgres/create-a-table-from-the-structure-of-another.md) - [Create An Index Across Two Columns](postgres/create-an-index-across-two-columns.md) - [Create An Index Without Locking The Table](postgres/create-an-index-without-locking-the-table.md) - [Create And Execute SQL Statements With \gexec](postgres/create-and-execute-sql-statements-with-gexec.md) - [Create Database Uses Template1](postgres/create-database-uses-template1.md) - [Create hstore From Two Arrays](postgres/create-hstore-from-two-arrays.md) - [Create Table Adds A Data Type](postgres/create-table-adds-a-data-type.md) - [Creating Conditional Constraints](postgres/creating-conditional-constraints.md) - [Creating Custom Types](postgres/creating-custom-types.md) - [Day Of Week By Name For A Date](postgres/day-of-week-by-name-for-a-date.md) - [Day Of Week For A Date](postgres/day-of-week-for-a-date.md) - [Default Schema](postgres/default-schema.md) - [Defining Arrays](postgres/defining-arrays.md) - [Determine Types Of JSONB Records](postgres/determine-types-of-jsonb-records.md) - [Determining The Age Of Things](postgres/determining-the-age-of-things.md) - [Difference Between Explain And Explain Analyze](postgres/difference-between-explain-and-explain-analyze.md) - [Different Ways To Define An Interval](postgres/different-ways-to-define-an-interval.md) - [Dump All Databases To A SQL File](postgres/dump-all-databases-to-a-sql-file.md) - [Dump And Restore A Database](postgres/dump-and-restore-a-database.md) - [Dump The SQL Needed To Recreate A Table](postgres/dump-the-sql-needed-recreate-a-table.md) - [Duplicate A Local Database](postgres/duplicate-a-local-database.md) - [Edit Existing Functions](postgres/edit-existing-functions.md) - [Enable Logging Of Database Activity](postgres/enable-logging-of-database-activity.md) - [Enforce Uniqueness On Column Expression](postgres/enforce-uniqueness-on-column-expression.md) - [Escaping A Quote In A String](postgres/escaping-a-quote-in-a-string.md) - [Escaping String Literals With Dollar Quoting](postgres/escaping-string-literals-with-dollar-quoting.md) - [Export Query Results To A CSV](postgres/export-query-results-to-a-csv.md) - [Extracting Nested JSON Data](postgres/extracting-nested-json-data.md) - [Fetch Data From An Endpoint In SQL](postgres/fetch-data-from-an-endpoint-in-sql.md) - [Fetch Specific Number Of Results](postgres/fetch-specific-number-of-results.md) - [Find Duplicate Records In Table Without Unique Id](postgres/find-duplicate-records-in-table-without-unique-id.md) - [Find Records That Contain Duplicate Values](postgres/find-records-that-contain-duplicate-values.md) - [Find Records That Have Multiple Associated Records](postgres/find-records-that-have-multiple-associated-records.md) - [Find The Data Directory](postgres/find-the-data-directory.md) - [Find The Location Of Postgres Config Files](postgres/find-the-location-of-postgres-config-files.md) - [Fizzbuzz With Common Table Expressions](postgres/fizzbuzz-with-common-table-expressions.md) - [Force SSL When Making A psql Connection](postgres/force-ssl-when-making-a-psql-connection.md) - [Generate A UUID](postgres/generate-a-uuid.md) - [Generate Modern Primary Key Columns](postgres/generate-modern-primary-key-columns.md) - [Generate Random Alphanumeric Identifier](postgres/generate-random-alphanumeric-identifier.md) - [Generate Random UUIDs Without An Extension](postgres/generate-random-uuids-without-an-extension.md) - [Generate Series Of Numbers](postgres/generate-series-of-numbers.md) - [Generating UUIDs With pgcrypto](postgres/generating-uuids-with-pgcrypto.md) - [Get A Quick Approximate Count Of A Table](postgres/get-a-quick-approximate-count-of-a-table.md) - [Get Row Count For Most Recent Query](postgres/get-row-count-for-most-recent-query.md) - [Get The Size On Disk of An Index](postgres/get-the-size-on-disk-of-an-index.md) - [Get The Size Of A Database](postgres/get-the-size-of-a-database.md) - [Get The Size Of A Table](postgres/get-the-size-of-a-table.md) - [Get The Size Of An Index](postgres/get-the-size-of-an-index.md) - [Getting A Slice Of An Array](postgres/getting-a-slice-of-an-array.md) - [Group By The Result Of A Function Call](postgres/group-by-the-result-of-a-function-call.md) - [Idempotent Inserts](postgres/idempotent-inserts.md) - [Include All Queries In The Log File](postgres/include-all-queries-in-the-log-file.md) - [Include Columns In A Covering Index](postgres/include-columns-in-a-covering-index.md) - [Include Multiple Tables In A pg_dump](postgres/include-multiple-tables-in-a-pg-dump.md) - [Insert A Bunch Of Records With Generate Series](postgres/insert-a-bunch-of-records-with-generate-series.md) - [Insert Just The Defaults](postgres/insert-just-the-defaults.md) - [Inspect Progress Of Long-Running Create Index](postgres/inspect-progress-of-long-running-create-index.md) - [Install Postgres With uuid-ossp Using asdf](postgres/install-postgres-with-uuid-ossp-using-asdf.md) - [Integers In Postgres](postgres/integers-in-postgres.md) - [Intervals Of Time By Week](postgres/intervals-of-time-by-week.md) - [Is It Null Or Not Null?](postgres/is-it-null-or-not-null.md) - [Label Dollar-Quoted Strings With A Tag](postgres/label-dollar-quoted-strings-with-a-tag.md) - [Limit Execution Time Of Statements](postgres/limit-execution-time-of-statements.md) - [List All Columns Of A Specific Type](postgres/list-all-columns-of-a-specific-type.md) - [List All Rows In A Table](postgres/list-all-rows-in-a-table.md) - [List All The Databases](postgres/list-all-the-databases.md) - [List All Versions Of A Function](postgres/list-all-versions-of-a-function.md) - [List Available Schemas](postgres/list-available-schemas.md) - [List Connections To A Database](postgres/list-connections-to-a-database.md) - [List Databases Available For Connecting](postgres/list-databases-available-for-connecting.md) - [List Database Objects With Disk Usage](postgres/list-database-objects-with-disk-usage.md) - [List Database Users](postgres/list-database-users.md) - [List Various Kinds Of Objects](postgres/list-various-kinds-of-objects.md) - [Lower Is Faster Than ilike](postgres/lower-is-faster-than-ilike.md) - [Manage Major Versions With Brew And Direnv](postgres/manage-major-versions-with-brew-and-direnv.md) - [Max Identifier Length Is 63 Bytes](postgres/max-identifier-length-is-63-bytes.md) - [Open Heroku Database In Postico From Terminal](postgres/open-heroku-database-in-postico-from-terminal.md) - [Output Explain Query Plan In Different Formats](postgres/output-explain-query-plan-in-different-formats.md) - [pg Prefix Is Reserved For System Schemas](postgres/pg-prefix-is-reserved-for-system-schemas.md) - [Postgres Does Not Support Unsigned Integers](postgres/postgres-does-not-support-unsigned-integers.md) - [Prepare, Execute, And Deallocate Statements](postgres/prepare-execute-and-deallocate-statements.md) - [Pretty Print Data Sizes](postgres/pretty-print-data-sizes.md) - [Pretty Printing JSONB Rows](postgres/pretty-printing-jsonb-rows.md) - [Prevent A Query From Running Too Long](postgres/prevent-a-query-from-running-too-long.md) - [Print The Query Buffer In psql](postgres/print-the-query-buffer-in-psql.md) - [Put Unique Constraint On Generated Column](postgres/put-unique-constraint-on-generated-column.md) - [References Target Primary Key By Default](postgres/references-target-primary-key-by-default.md) - [Remove Not Null Constraint From A Column](postgres/remove-not-null-constraint-from-a-column.md) - [Renaming A Sequence](postgres/renaming-a-sequence.md) - [Renaming A Table](postgres/renaming-a-table.md) - [Restart A Sequence](postgres/restart-a-sequence.md) - [Restarting Sequences When Truncating Tables](postgres/restarting-sequences-when-truncating-tables.md) - [Salt And Hash A Password With pgcrypto](postgres/salt-and-hash-a-password-with-pgcrypto.md) - [Send A Command To psql](postgres/send-a-command-to-psql.md) - [Set Inclusion With hstore](postgres/set-inclusion-with-hstore.md) - [Set A Seed For The Random Number Generator](postgres/set-a-seed-for-the-random-number-generator.md) - [Set A Statement Timeout Threshold For A Session](postgres/set-a-statement-timeout-threshold-for-a-session.md) - [Set Up A Project-Local Cluster With Postgres.app](postgres/set-up-a-project-local-cluster-with-postgres-app.md) - [Sets With The Values Command](postgres/sets-with-the-values-command.md) - [Shorthand Absolute Value Operator](postgres/shorthand-absolute-value-operator.md) - [Show All Versions Of An Operator](postgres/show-all-versions-of-an-operator.md) - [Show Reconstructed Constraints For A Table](postgres/show-reconstructed-constraints-for-a-table.md) - [Show The Hidden Queries Behind Backslash Commands](postgres/show-the-hidden-queries-behind-backslash-commands.md) - [Sleeping](postgres/sleeping.md) - [Special Math Operators](postgres/special-math-operators.md) - [Storing Emails With citext](postgres/storing-emails-with-citext.md) - [String Contains Another String](postgres/string-contains-another-string.md) - [Survey Of User-Defined Ordering Of Records](postgres/survey-of-user-defined-ordering-of-records.md) - [Switch Non-Castable Column Type With Using Clause](postgres/switch-non-castable-column-type-with-using-clause.md) - [Switch The Running Postgres Server Version](postgres/switch-the-running-postgres-server-version.md) - [Table Names Are Treated As Lower-Case By Default](postgres/table-names-are-treated-as-lower-case-by-default.md) - [Temporarily Disable Triggers](postgres/temporarily-disable-triggers.md) - [Temporary Tables](postgres/temporary-tables.md) - [Terminating A Connection](postgres/terminating-a-connection.md) - [The nullif Function](postgres/the-nullif-function.md) - [Timestamp Functions](postgres/timestamp-functions.md) - [Toggling The Pager In PSQL](postgres/toggling-the-pager-in-psql.md) - [Track psql History Separately Per Database](postgres/track-psql-history-separately-per-database.md) - [Trim Leading And Trailing Space From String](postgres/trim-leading-and-trailing-space-from-string.md) - [Truncate All Rows](postgres/truncate-all-rows.md) - [Truncate Tables With Dependents](postgres/truncate-tables-with-dependents.md) - [Turning Timing On](postgres/turn-timing-on.md) - [Two Ways To Compute Factorial](postgres/two-ways-to-compute-factorial.md) - [Two Ways To Escape A Quote In A String](postgres/two-ways-to-escape-a-quote-in-a-string.md) - [Types By Category](postgres/types-by-category.md) - [Unable To Infer Data Type In Production](postgres/unable-to-infer-data-type-in-production.md) - [Union All Rows Including Duplicates](postgres/union-all-rows-including-duplicates.md) - [Use A psqlrc File For Common Settings](postgres/use-a-psqlrc-file-for-common-settings.md) - [Use A Trigger To Mirror Inserts To Another Table](postgres/use-a-trigger-to-mirror-inserts-to-another-table.md) - [Use Argument Indexes](postgres/use-argument-indexes.md) - [Use Not Valid To Immediately Enforce A Constraint](postgres/use-not-valid-to-immediately-enforce-a-constraint.md) - [Use Rename To Hot Swap Two Tables](postgres/use-rename-to-hot-swap-two-tables.md) - [Use Variables In An Anonymous Function](postgres/use-variables-in-an-anonymous-function.md) - [Using Expressions In Indexes](postgres/using-expressions-in-indexes.md) - [Using Intervals To Offset Time](postgres/using-intervals-to-offset-time.md) - [Who Is The Current User](postgres/who-is-the-current-user.md) - [Word Count for a Column](postgres/word-count-for-a-column.md) - [Write A Query Result To File](postgres/write-a-query-result-to-file.md) ### Prisma - [Apply Separate Formatting With A Blank Line](prisma/apply-separate-formatting-with-a-blank-line.md) - [Batch Insert Records With createMany](prisma/batch-insert-records-with-create-many.md) - [Check If Database And Schema Are Not In Sync](prisma/check-if-database-and-schema-are-not-in-sync.md) - [Configure Client To Log SQL Queries](prisma/configure-client-to-log-sql-queries.md) - [Execute A Raw SQL Query](prisma/execute-a-raw-sql-query.md) - [Grab A Limited Set Of Records](prisma/grab-a-limited-set-of-records.md) - [Open Connections To Multiple Databases](prisma/open-connections-to-multiple-databases.md) - [Override Table Name For Prisma Model](prisma/override-table-name-for-prisma-model.md) - [Specify Alternate Location For Prisma Schema](prisma/specify-alternate-location-for-prisma-schema.md) ### Python - [Access Instance Variables](python/access-instance-variables.md) - [Access Most Recent Return Value In REPL](python/access-most-recent-return-value-in-repl.md) - [Break Debugger On First Line Of Program](python/break-debugger-on-first-line-of-program.md) - [Check If Package Is Installed With Pip](python/check-if-package-is-installed-with-pip.md) - [Control Passing Of Time In Tests](python/control-passing-of-time-in-tests.md) - [Create A Dummy DataFrame In Pandas](python/create-a-dummy-dataframe-in-pandas.md) - [Create A Range Of Descending Values](python/create-a-range-of-descending-values.md) - [Dunder Methods](python/dunder-methods.md) - [Easy Key-Value Aggregates With defaultdict](python/easy-key-value-aggregates-with-defaultdict.md) - [Install With PIP For Specific Interpreter](python/install-with-pip-for-specific-interpreter.md) - [Iterate First N Items From Enumerable](python/iterate-first-n-items-from-enumerable.md) - [Iterate Over A Dictionary](python/iterate-over-a-dictionary.md) - [Keep A Tally With collections.Counter](python/keep-a-tally-with-collections-counter.md) - [Load A File Into The Python REPL](python/load-a-file-into-the-python-repl.md) - [Look Inside Pytest tmp_path](python/look-inside-pytest-tmp-path.md) - [Override The Boolean Context Of A Class](python/override-the-boolean-context-of-a-class.md) - [Parse Relative Time To datetime Object](python/parse-relative-time-to-datetime-object.md) - [Store And Access Immutable Data In A Tuple](python/store-and-access-immutable-data-in-a-tuple.md) - [Test A Function With Pytest](python/test-a-function-with-pytest.md) - [Use pipx To Install End User Apps](python/use-pipx-to-install-end-user-apps.md) - [Use Verbose Flag To Get More Diff](python/use-verbose-flag-to-get-more-diff.md) ### Rails - [Access Secrets In A Rails 5.2 App](rails/access-secrets-in-a-rails-5-2-app.md) - [ActiveRecord Query For This Or That](rails/active-record-query-for-this-or-that.md) - [Add A Check Constraint To A Table](rails/add-a-check-constraint-to-a-table.md) - [Add A Database Index If It Does Not Already Exist](rails/add-a-database-index-if-it-does-not-already-exist.md) - [Add A Foreign Key Reference To A Table](rails/add-a-foreign-key-reference-to-a-table.md) - [Add A Generated Column To A PostgreSQL Table](rails/add-a-generated-column-to-a-postgresql-table.md) - [Add A Reference Column With An Index](rails/add-a-reference-column-with-an-index.md) - [Add ActiveRecord Error Not Tied To Any Attribute](rails/add-activerecord-error-not-tied-to-any-attribute.md) - [Add Color To The IRB Console Prompt](rails/add-color-to-the-irb-console-prompt.md) - [Add React With Webpacker To A New Rails App](rails/add-react-with-webpacker-to-a-new-rails-app.md) - [Add timestamptz Columns With The Migration DSL](rails/add-timestamptz-columns-with-the-migration-dsl.md) - [Adjust The Production Log Level](rails/adjust-the-production-log-level.md) - [Advance The Date](rails/advance-the-date.md) - [Allow Associations To Be Optional](rails/allow-associations-to-be-optional.md) - [Allow List Params Anywhere With Strong Params](rails/allow-list-params-anywhere-with-strong-params.md) - [All or Nothing Database Transactions](rails/all-or-nothing-database-transactions.md) - [Alphabetize Schema Columns To Keep Them Consistent](rails/alphabetize-schema-columns-to-keep-them-consistent.md) - [Alter The Rails Setup Script](rails/alter-the-rails-setup-script.md) - [Apply Basic HTML Formatting To Block Of Text](rails/apply-basic-html-formatting-to-block-of-text.md) - [Assert Two Arrays Have The Same Items With RSpec](rails/assert-two-arrays-have-the-same-items-with-rspec.md) - [Attach A File With Capybara](rails/attach-a-file-with-capybara.md) - [Attribute Getter without the Recursion](rails/attribute-getter-without-the-recursion.md) - [Attribute Was](rails/attribute-was.md) - [Autosave False On ActiveRecord Associations](rails/autosave-false-on-activerecord-associations.md) - [Bind Parameters To ActiveRecord SQL Query](rails/bind-parameters-to-activerecord-sql-query.md) - [Build A Hash Of Model Attributes](rails/build-a-hash-of-model-attributes.md) - [Capture Development Emails With Mailhog](rails/capture-development-emails-with-mailhog.md) - [Capybara Page Status Code](rails/capybara-page-status-code.md) - [Cast Common Boolean-Like Values To Booleans](rails/cast-common-boolean-like-values-to-booleans.md) - [Change The Nullability Of A Column](rails/change-the-nullability-of-a-column.md) - [Change The Time Zone Offset Of A DateTime Object](rails/change-the-time-zone-offset-of-a-datetime-object.md) - [Check How Database Is Configured](rails/check-how-database-is-configured.md) - [Check If ActiveRecord Update Fails](rails/check-if-activerecord-update-fails.md) - [Check If Any Records Have A Null Value](rails/check-if-any-records-have-a-null-value.md) - [Check Specific Attributes On ActiveRecord Array](rails/check-specific-attributes-on-activerecord-array.md) - [Check The Current Named Log Level](rails/check-the-current-named-log-level.md) - [Clean Up Memory Hungry Rails Console Processes](rails/clean-up-memory-hungry-rails-console-processes.md) - [Code Statistics For An Application](rails/code-statistics-for-an-application.md) - [Columns With Default Values Are Nil On Create](rails/columns-with-default-values-are-nil-on-create.md) - [Comparing DateTimes Down To Second Precision](rails/comparing-datetimes-down-to-second-precision.md) - [Conditional Class Selectors in Haml](rails/conditional-class-selectors-in-haml.md) - [Convert A Symbol To A Constant](rails/convert-a-symbol-to-a-constant.md) - [Convert JSON Field To Hash With Indifferent Access](rails/convert-json-field-to-hash-with-indifferent-access.md) - [Count The Number Of Records By Attribute](rails/count-the-number-of-records-by-attribute.md) - [Create A Custom Named References Column](rails/create-a-custom-named-references-column.md) - [Create A Join Table With The Migration DSL](rails/create-a-join-table-with-the-migration-dsl.md) - [Create Table With bigint Id As Primary Key](rails/create-table-with-bigint-id-as-primary-key.md) - [Creating Records of Has_One Associations](rails/creating-records-of-has-one-associations.md) - [Custom Validation Message](rails/custom-validation-message.md) - [Customize Paths And Helpers For Devise Routes](rails/customize-paths-and-helpers-for-devise-routes.md) - [Customize Template For New Schema Migration](rails/customize-template-for-new-schema-migration.md) - [Customize The Path Of A Resource Route](rails/customize-the-path-of-a-resource-route.md) - [Define The Root Path For The App](rails/define-the-root-path-for-the-app.md) - [Delete Paranoid Records](rails/delete-paranoid-records.md) - [Demodulize A Class Name](rails/demodulize-a-class-name.md) - [Determine The Configured Primary Key Type](rails/determine-the-configured-primary-key-type.md) - [Different Ways To Add A Foreign Key Reference](rails/different-ways-to-add-a-foreign-key-reference.md) - [Disambiguate Where In A Joined Relation](rails/disambiguate-where-in-a-joined-relation.md) - [Empty find_by Returns First Record](rails/empty-find-by-returns-first-record.md) - [Enforce Locals Passed To A Partial](rails/enforce-locals-passed-to-a-partial.md) - [Ensure A Rake Task Cannot Write Data](rails/ensure-a-rake-task-cannot-write-data.md) - [Ensure Migrations Use The Latest Schema](rails/ensure-migrations-use-the-latest-schema.md) - [Ensure Record Saved With after_commit Callback](rails/ensure-record-saved-with-after-commit-callback.md) - [Filter ActiveModel Validation Errors](rails/filter-active-model-validation-errors.md) - [Filter ActiveStorage Blobs To Only Images](rails/filter-active-storage-blobs-to-only-images.md) - [Find Or Create A Record With FactoryBot](rails/find-or-create-a-record-with-factory-bot.md) - [Find Records With Multiple Associated Records](rails/find-records-with-multiple-associated-records.md) - [Force All Users To Sign Out](rails/force-all-users-to-sign-out.md) - [Format DateTime With Builtin Formats](rails/format-datetime-with-builtin-formats.md) - [Format Specific html.erb Template Files](rails/format-specific-html-erb-template-files.md) - [Generate A Model](rails/generate-a-model.md) - [Generate A Rails App From The Main Branch](rails/generate-a-rails-app-from-the-main-branch.md) - [Generating And Executing SQL](rails/generating-and-executing-sql.md) - [Get A Quick Approximate Count Of A Large Table](rails/get-a-quick-approximate-count-of-a-large-table.md) - [Get ActiveRecord Attribute Directly From Database](rails/get-active-record-attribute-directly-from-database.md) - [Get An Array Of Values From The Database](rails/get-an-array-of-values-from-the-database.md) - [Get An Empty ActiveRecord Relation](rails/get-an-empty-activerecord-relation.md) - [Get Formatted UTC Offset Value](rails/get-formatted-utc-offset-value.md) - [Get Help With A Rails App Update](rails/get-help-with-a-rails-app-update.md) - [Get The Column Names For A Model](rails/get-the-column-names-for-a-model.md) - [Get The Current Time](rails/get-the-current-time.md) - [Grab A Random Record From The Database](rails/grab-a-random-record-from-the-database.md) - [Handle Named Arguments In A Rake Task](rails/handle-named-arguments-in-a-rake-task.md) - [Hash Slicing](rails/hash-slicing.md) - [Ignore Poltergeist JavaScript Errors](rails/ignore-poltergeist-javascript-errors.md) - [Include Devise Helpers In Your Controller Tests](rails/include-devise-helpers-in-your-controller-tests.md) - [Inspect Configuration Of Database Connection](rails/inspect-configuration-of-database-connection.md) - [Inspect Previous Changes To ActiveRecord Object](rails/inspect-previous-changes-to-activerecord-object.md) - [Link To The Current Page With Query Params](rails/link-to-the-current-page-with-query-params.md) - [List All Installable Rails Versions](rails/list-all-installable-rails-versions.md) - [List The Enqueued Jobs](rails/list-the-enqueued-jobs.md) - [Load A File When Starting Rails Console](rails/load-a-file-when-starting-rails-console.md) - [Load Records In Batches With find_each](rails/load-records-in-batches-with-find-each.md) - [Log SQL Queries Executed By ActiveRecord](rails/log-sql-queries-executed-by-activerecord.md) - [Look Up Time Zone Info For Identifier](rails/look-up-time-zone-info-for-identifier.md) - [Mark A Migration As Irreversible](rails/mark-a-migration-as-irreversible.md) - [Make A String Attribute Easy To Inquire About](rails/make-a-string-attribute-easy-to-inquire-about.md) - [Make ActionMailer Synchronous In Test](rails/make-action-mailer-synchronous-in-test.md) - [Make Remove Column Migration Reversible](rails/make-remove-column-migration-reversible.md) - [Manage Timestamps With Upsert](rails/manage-timestamps-with-upsert.md) - [Manually Run A Migration From Rails Console](rails/manually-run-a-migration-from-rails-console.md) - [Mark For Destruction](rails/mark-for-destruction.md) - [Mask An ActiveRecord Attribute](rails/mask-an-activerecord-attribute.md) - [Merge A Scope Into An ActiveRecord Query](rails/merge-a-scope-into-an-activerecord-query.md) - [Migrating Up Down Up](rails/migrating-up-down-up.md) - [Mock Rails Environment With An Inquiry Instance](rails/mock-rails-environment-with-an-inquiry-instance.md) - [Order Matters For `rescue_from` Blocks](rails/order-matters-for-rescue-from-blocks.md) - [Override Text Displayed By Form Label](rails/override-text-displayed-by-form-label.md) - [Parameterize A String With Underscores](rails/parameterize-a-string-with-underscores.md) - [Params Includes Submission Button Info](rails/params-includes-submission-button-info.md) - [Params Is A Hash With Indifferent Access](rails/params-is-a-hash-with-indifferent-access.md) - [Parse Query Params From A URL](rails/parse-query-params-from-a-url.md) - [Parse Request Params In Rack::Attack Block](rails/parse-request-params-in-rack-attack-block.md) - [Perform SQL Explain With ActiveRecord](rails/perform-sql-explain-with-activerecord.md) - [Polymorphic Path Helpers](rails/polymorphic-path-helpers.md) - [Prefer select_all Over execute For Read Queries](rails/prefer-select-all-over-execute-for-read-queries.md) - [Pretend Generations](rails/pretend-generations.md) - [Prevent Mailer Previews From Cluttering Database](rails/prevent-mailer-previews-from-cluttering-database.md) - [Prevent Writes With A Sandboxed Rails Console](rails/prevent-writes-with-a-sandboxed-rails-console.md) - [Provide Fake Form Helper To Controllers](rails/provide-fake-form-helper-to-controllers.md) - [Query A Single Value From The Database](rails/query-a-single-value-from-the-database.md) - [Read In Environment-Specific Config Values](rails/read-in-environment-specific-config-values.md) - [Read-Only Models](rails/read-only-models.md) - [Rebuild Tailwind Bundle For Dev Server](rails/rebuild-tailwind-bundle-for-dev-server.md) - [Remove A Database Column From A Table](rails/remove-a-database-column-from-a-table.md) - [Remove The Default Value On A Column](rails/remove-the-default-value-on-a-column.md) - [Render An Alternative ActionMailer Template](rails/render-an-alternative-action-mailer-template.md) - [Render The Response Body In Controller Specs](rails/render-the-response-body-in-controller-specs.md) - [Replace An Index With A Unique Index](rails/replace-an-index-with-a-unique-index.md) - [Rescue From](rails/rescue-from.md) - [Rescue From With A Separate Method](rails/rescue-from-with-a-separate-method.md) - [Respond With JSON Regardless of Content Type](rails/respond-with-json-regardless-of-content-type.md) - [Restart Puma Server By Touching Restart File](rails/restart-puma-server-by-touching-restart-file.md) - [Retrieve An Object If It Exists](rails/retrieve-an-object-if-it-exists.md) - [Rollback A Couple Migrations](rails/rollback-a-couple-migrations.md) - [Rollback A Specific Migration Out Of Order](rails/rollback-a-specific-migration-out-of-order.md) - [Rounding Numbers With Precision](rails/rounding-numbers-with-precision.md) - [Run A Rake Task Programmatically](rails/run-a-rake-task-programmatically.md) - [Run Commands With Specific Rails Version](rails/run-commands-with-specific-rails-version.md) - [Run Dev Processes With Overmind Instead Of Foreman](rails/run-dev-processes-with-overmind-instead-of-foreman.md) - [Run Rails Console With Remote Dokku App](rails/run-rails-console-with-remote-dokku-app.md) - [Run Some Code Whenever Rails Console Starts](rails/run-some-code-whenever-rails-console-starts.md) - [Scaffold Auth Functionality With Rails 8 Generator](rails/scaffold-auth-functionality-with-rails-8-generator.md) - [Schedule Sidekiq Jobs Out Into The Future](rails/schedule-sidekiq-jobs-out-into-the-future.md) - [Scope Records To A Lower Or Upper Bound](rails/scope-records-to-a-lower-or-upper-bound.md) - [Secure Passwords With Rails And Bcrypt](rails/secure-passwords-with-rails-and-bcrypt.md) - [Select A Select By Selector](rails/select-a-select-by-selector.md) - [Select A Specific Rails Version To Install](rails/select-a-specific-rails-version-to-install.md) - [Select Value For SQL Counts](rails/select-value-for-sql-counts.md) - [Serialize With fast_jsonapi In A Rails App](rails/serialize-with-fast-jsonapi-in-a-rails-app.md) - [Set A Timestamp Field To The Current Time](rails/set-a-timestamp-field-to-the-current-time.md) - [Set DateTime To Include Time Zone In Migrations](rails/set-datetime-to-include-time-zone-in-migrations.md) - [Set Default As SQL Function In Migration](rails/set-default-as-sql-function-in-migration.md) - [Set default_url_options For Entire Application](rails/set-default-url-options-for-entire-application.md) - [Set Meta Tags In ERB Views](rails/set-meta-tags-in-erb-views.md) - [Set Schema Search Path](rails/set-schema-search-path.md) - [Set Statement Timeout For All Postgres Connections](rails/set-statement-timeout-for-all-postgres-connections.md) - [Set The Default Development Port](rails/set-the-default-development-port.md) - [Show Pending Migrations](rails/show-pending-migrations.md) - [Show Rails Models With Pry](rails/show-rails-models-with-pry.md) - [Show Rails Routes With Pry](rails/show-rails-routes-with-pry.md) - [Skip Validations When Creating A Record](rails/skip-validations-when-creating-a-record.md) - [Specify New Attributes For #find_or_create_by](rails/specify-new-attributes-for-find-or-create-by.md) - [Temporarily Disable strong_params](rails/temporarily-disable-strong-params.md) - [Temporarily Turn Off Pending Migrations Error](rails/temporarily-turn-off-pending-migrations-error.md) - [Test For A Subset Of Attributes On A Model](rails/test-for-a-subset-of-attributes-on-a-model.md) - [Test If An Instance Variable Was Assigned](rails/test-if-an-instance-variable-was-assigned.md) - [Test If deliver_later Is Called For A Mailer](rails/test-if-deliver-later-is-called-for-a-mailer.md) - [Test Out URL And Path Helpers In The Console](rails/test-out-url-and-path-helpers-in-the-console.md) - [Truncate Almost All Tables](rails/truncate-almost-all-tables.md) - [Update Column Versus Update Attribute](rails/update-column-versus-update-attribute.md) - [Upgrading Your Manifest For Sprocket's 4](rails/upgrading-your-manifest-for-sprockets-4.md) - [Use IRB And Ruby Flags With Rails Console](rails/use-irb-and-ruby-flags-with-rails-console.md) - [Use .ruby Extension For Template File](rails/use-ruby-extension-for-template-file.md) - [Useful ActiveSupport Constants For Durations](rails/useful-active-support-constants-for-durations.md) - [Validate Column Data With Check Constraints](rails/validate-column-data-with-check-constraints.md) - [Verify And Read A Signed Cookie Value](rails/verify-and-read-a-signed-cookie-value.md) - [Where Am I In The Partial Iteration?](rails/where-am-i-in-the-partial-iteration.md) - [Why Redirect And Return In Controllers](rails/why-redirect-and-return-in-controllers.md) - [Wipe Out All Precompiled Assets](rails/wipe-out-all-precompiled-assets.md) - [Write Reversible Migration To Set Default](rails/write-reversible-migration-to-set-default.md) - [Write Safer Where Clauses With Placeholders](rails/write-safer-where-clauses-with-placeholders.md) ### React - [A Component Is Just A Bag Of Data](react/a-component-is-just-a-bag-of-data.md) - [Access The Latest Lifecycle Methods In An Old App](react/access-the-latest-lifecycle-methods-in-an-old-app.md) - [Accessing Env Vars In create-react-app](react/accessing-env-vars-in-create-react-app.md) - [Accessing Location Within @reach/router](react/accessing-location-within-reach-router.md) - [Allow md As An Extension With gatsby-mdx](react/allow-md-as-an-extension-with-gatsby-mdx.md) - [Alter The Display Name Of A Component](react/alter-the-display-name-of-a-component.md) - [Building A React App In The Browser](react/building-a-react-app-in-the-browser.md) - [Check The Type Of A Child Component](react/check-the-type-of-a-child-component.md) - [Conditionally Including Event Handler Functions](react/conditionally-including-event-handler-functions.md) - [Create A Snowpack-Bundled React App](react/create-a-snowpack-bundled-react-app.md) - [Create Dynamically Named Custom React Components](react/create-dynamically-named-custom-react-components.md) - [create-react-app Comes With Lodash](react/create-react-app-comes-with-lodash.md) - [create-react-app Has A Default Test Setup File](react/create-react-app-has-a-default-test-setup-file.md) - [CSS !important Is Not Supported By Inline Styles](react/css-important-is-not-supported-by-inline-styles.md) - [Debug Jest Tests In create-react-app](react/debug-jest-tests-in-create-react-app.md) - [Defining State In A Simple Class Component](react/defining-state-in-a-simple-class-component.md) - [Destructure Variables As Props To A Component](react/destructure-variables-as-props-to-a-component.md) - [Details Tags Are A Controllable Component](react/details-tags-are-a-controllable-component.md) - [Dispatch Anywhere With Redux](react/dispatch-anywhere-with-redux.md) - [Dynamically Add Props To A Child Component](react/dynamically-add-props-to-a-child-component.md) - [Dynamically Create HTML Elements](react/dynamically-create-html-elements.md) - [Enforce Specific Values With PropTypes](react/enforce-specific-values-with-proptypes.md) - [Focus An Input With useRef Hook](react/focus-an-input-with-useref-hook.md) - [Force A Component To Only Have One Child](react/force-a-component-to-only-have-one-child.md) - [Forcing A Child Remount With The Key Prop](react/forcing-a-child-remount-with-the-key-prop.md) - [Formik Connected Components](react/formik-connected-components.md) - [Formik's Validation Schema As A Function](react/formiks-validation-schema-as-a-function.md) - [Inactive And Active Component Styles With Radium](react/inactive-and-active-component-styles-with-radium.md) - [Inline Style Attributes Should Be Camel Cased](react/inline-style-attributes-should-be-camel-cased.md) - [Manage State In A Functional Component](react/manage-state-in-a-functional-component.md) - [Mapping Over One Or Many Children](react/mapping-over-one-or-many-children.md) - [Mock A Function That A Component Imports](react/mock-a-function-that-a-component-imports.md) - [Navigate With State Via @reach/router](react/navigate-with-state-via-reach-router.md) - [Pairing A Callback With A useState Hook](react/pairing-a-callback-with-a-usestate-hook.md) - [Pass A Function To A useState Updater](react/pass-a-function-to-a-usestate-updater.md) - [Passing Props Down To React-Router Route](react/passing-props-down-to-react-router-route.md) - [Prevent reach/router Redirect Error Screen In Dev](react/prevent-reach-router-redirect-error-screen-in-dev.md) - [Proxy To An API Server In Development With CRA](react/proxy-to-an-api-server-in-development-with-cra.md) - [Quickly Search For A Component With React DevTools](react/quickly-search-for-a-component-with-react-devtools.md) - [@reach/router Renders To A Div](react/reach-router-renders-to-a-div.md) - [Read Only Input Elements](react/read-only-input-elements.md) - [Rendering Multiple Nodes With Fragments](react/rendering-multiple-nodes-with-fragments.md) - [Set The Type For A useState Hook](react/set-the-type-for-a-usestate-hook.md) - [Specifying Dependencies Of A useEffect Hook](react/specifying-dependencies-of-a-useeffect-hook.md) - [Spelunking Through Components With Enzyme's Dive](react/spelunking-through-components-with-enzymes-dive.md) - [Sync Your react-router State With Redux](react/sync-your-react-router-state-with-redux.md) - [Test Files In create-react-app](react/test-files-in-create-react-app.md) - [Test That Element Does Not Render In The Component](react/test-that-element-does-not-render-in-the-component.md) - [Trigger Effect Only When The Component Mounts](react/trigger-effect-only-when-the-component-mounts.md) - [Update Formik Initial Values When Props Change](react/update-formik-initial-values-when-props-change.md) - [Upgrading To The Latest React In CodeSandbox](react/upgrading-to-the-latest-react-in-codesandbox.md) - [Use A Ref To Autofocus An Input](react/use-a-ref-to-autofocus-an-input.md) - [Use React 16 With Gatsby](react/use-react-16-with-gatsby.md) - [Use withRouter To Pass Down React-Router History](react/use-withrouter-to-pass-down-react-router-history.md) - [Visually Select A React Element For Inspection](react/visually-select-a-react-element-for-inspection.md) - [Who Is Your Favorite Child?](react/who-is-your-favorite-child.md) - [Wrap The Root Of A Gatsby App In A Component](react/wrap-the-root-of-a-gatsby-app-in-a-component.md) ### React Native - [Avoid The Notch With SafeAreaView](react_native/avoid-the-notch-with-safeareaview.md) ### React Testing Library - [Check That A Component Renders As Null](react-testing-library/check-that-a-component-renders-as-null.md) - [findBy\* Queries Have Async Built In](react-testing-library/find-by-queries-have-async-built-in.md) - [Pretty Print Some DOM To Debug A Test](react-testing-library/pretty-print-some-dom-to-debug-a-test.md) - [Test A Component That Uses React Portals](react-testing-library/test-a-component-that-uses-react-portals.md) ### ReasonML - [Break Out Of A While Loop](reason/break-out-of-a-while-loop.md) - [Compile Reason To Native With Dune](reason/compile-reason-to-native-with-dune.md) - [Compile Reason With An OCaml Package Using Dune](reason/compile-reason-with-an-ocaml-package-using-dune.md) - [Create A Map Of Strings](reason/create-a-map-of-strings.md) - [Create A Stream From An Array](reason/create-a-stream-from-an-array.md) - [Creating A 2D Array](reason/creating-a-2d-array.md) - [Data Structures With Self-Referential Types](reason/data-structures-with-self-referential-types.md) - [Defining Variants With Constructor Arguments](reason/defining-variants-with-constructor-arguments.md) - [Dynamically Create A Printf String Format](reason/dynamically-create-a-printf-string-format.md) - [Exhaustive Pattern Matching Of List Variants](reason/exhaustive-pattern-matching-of-list-variants.md) - [Format The Current File Within Vim](reason/format-the-current-file-within-vim.md) - [Generate A Native ReasonML Project With Pesy](reason/generate-a-native-reasonml-project-with-pesy.md) - [Generate Starter Reason Projects](reason/generate-starter-reason-projects.md) - [Helping The Compiler Help Us With Variants](reason/helping-the-compiler-help-us-with-variants.md) - [Inline Component Styles With Reason React](reason/inline-component-styles-with-reason-react.md) - [Is This A Directory Or A File?](reason/is-this-a-directory-or-a-file.md) - [Making Things Mutable](reason/making-things-mutable.md) - [Modifying A String With blit_string](reason/modifying-a-string-with-blit-string.md) - [Multi-Argument Functions As Syntactic Sugar](reason/multi-argument-functions-as-syntactic-sugar.md) - [Pattern Match On Exceptions](reason/pattern-match-on-exceptions.md) - [Quickly Bootstrap A React App Using Reason](reason/quickly-bootstrap-a-react-app-using-reason.md) - [Seeding And Generating Random Integers](reason/seeding-and-generating-random-integers.md) - [Stream A File Line By Line](reason/stream-a-file-line-by-line.md) - [String Interpolation With Integers And Sprintf](reason/string-interpolation-with-integers-and-sprintf.md) - [String Interpolation With Quoted Strings](reason/string-interpolation-with-quoted-strings.md) - [Trying Out ReasonML In CodeSandbox](reason/trying-out-reasonml-in-codesandbox.md) - [Two Ways To Find An Item In A List](reason/two-ways-to-find-an-item-in-a-list.md) - [Using Optional Labeled Function Arguments](reason/using-optional-labeled-function-arguments.md) - [Wrapping A Component For Use In JavaScript](reason/wrapping-a-component-for-use-in-javascript.md) ### Remix - [Get Query Params From The Request URL](remix/get-query-params-from-the-request-url.md) - [Markdown And MDX Files Are Rendered To Routes](remix/markdown-and-mdx-files-are-rendered-to-routes.md) - [Relative And Absolute Paths In Links](remix/relative-and-absolute-paths-in-links.md) - [Run The Development Server From Another Port](remix/run-the-development-server-from-another-port.md) - [Set The Title Of A Page](remix/set-the-title-of-a-page.md) ### RSpec - [Avoid Accidentally Disabling Pry](rspec/avoid-accidentally-disabling-pry.md) - [Check Specific Arguments To Received Method](rspec/check-specific-arguments-to-received-method.md) - [Configure Tests To Run In Random Order](rspec/configure-tests-to-run-in-random-order.md) - [Find Minimal Set Of Tests Causing A Flicker](rspec/find-minimal-set-of-tests-causing-a-flicker.md) - [Format Test Results As A JSON File](rspec/format-test-results-as-a-json-file.md) - [Run Tests With Documentation Formatting](rspec/run-tests-with-documentation-formatting.md) - [Use Specific Cache Store In A Single Test](rspec/use-specific-cache-store-in-a-single-test.md) ### Ruby - [A Basic Case Statement](ruby/a-basic-case-statement.md) - [A Shorthand For Rerunning Failed Tests With RSpec](ruby/a-shorthand-for-rerunning-failed-tests-with-rspec.md) - [Add Comments To Regex With Free-Spacing](ruby/add-comments-to-regex-with-free-spacing.md) - [Add Linux As A Bundler Platform](ruby/add-linux-as-a-bundler-platform.md) - [Add Progress Reporting To Long-Running Script](ruby/add-progress-reporting-to-long-running-script.md) - [Are They All True?](ruby/are-they-all-true.md) - [Assert About An Object's Attributes With RSpec](ruby/assert-about-an-objects-attributes-with-rspec.md) - [Assoc For Hashes](ruby/assoc-for-hashes.md) - [Audit Your Ruby Project For Any CVEs](ruby/audit-your-ruby-project-for-any-cves.md) - [Avoid Double Negation With Minitest Refute](ruby/avoid-double-negation-with-minitest-refute.md) - [Block Comments](ruby/block-comments.md) - [Block Syntaxes Have Different Precedence](ruby/block-syntaxes-have-different-precedence.md) - [Build HTTP And HTTPS URLs](ruby/build-http-and-https-urls.md) - [Chaining Multiple RSpec Change Matchers](ruby/chaining-multiple-rspec-change-matchers.md) - [Check For Any Overlaps In List Of Ranges](ruby/check-for-any-overlaps-in-list-of-ranges.md) - [Check If A URL Resolves To 200](ruby/check-if-a-url-resolves-to-200.md) - [Check If An Object Includes A Module](ruby/check-if-an-object-includes-a-module.md) - [Check Return Status Of Running A Shell Command](ruby/check-return-status-of-running-a-shell-command.md) - [Clamp To An Endless Range](ruby/clamp-to-an-endless-range.md) - [Click On Text With Capybara](ruby/click-on-text-with-capybara.md) - [Colorful Output With MiniTest](ruby/colorful-output-with-minitest.md) - [Comparing Class Hierarchy Relationships](ruby/comparing-class-hierarchy-relationships.md) - [Comparing Arrays In RSpec](ruby/comparing-arrays-in-rspec.md) - [Construct A Constant From A String](ruby/construct-a-constant-from-a-string.md) - [Convert A Unix Epoch Timestamp To A Time Object](ruby/convert-a-unix-epoch-timestamp-to-a-time-object.md) - [Create an Array of Stringed Numbers](ruby/create-an-array-of-stringed-numbers.md) - [Create a CSV::Table Object](ruby/create-a-csv-table-object.md) - [Create A Hash From An Array Of Arrays](ruby/create-a-hash-from-an-array-of-arrays.md) - [Create Listing Of All Middleman Pages](ruby/create-listing-of-all-middleman-pages.md) - [Create Mock Class That Can Be Overridden](ruby/create-mock-class-that-can-be-overridden.md) - [Create A Module Of Utility Functions](ruby/create-a-module-of-utility-functions.md) - [Create Named Structs With Struct.new](ruby/create-named-structs-with-struct-new.md) - [Create Thumbnail Image For A PDF](ruby/create-thumbnail-image-for-a-pdf.md) - [Decompose Unicode Character With Diacritic Mark](ruby/decompose-unicode-character-with-diacritic-mark.md) - [Defaulting To Frozen String Literals](ruby/defaulting-to-frozen-string-literals.md) - [Define A Custom RSpec Matcher](ruby/define-a-custom-rspec-matcher.md) - [Define A Method On A Struct](ruby/define-a-method-on-a-struct.md) - [Define Multiline Strings With Heredocs](ruby/define-multiline-strings-with-heredocs.md) - [Destructure The First Item From An Array](ruby/destructure-the-first-item-from-an-array.md) - [Destructuring Arrays In Blocks](ruby/destructuring-arrays-in-blocks.md) - [Disable Interpolation For A Heredoc String](ruby/disable-interpolation-for-a-heredoc-string.md) - [Disassemble Some Codes](ruby/disassemble-some-codes.md) - [Double Splat To Merge Hashes](ruby/double-splat-to-merge-hashes.md) - [Edit Previous Parts Of The Pry Buffer History](ruby/edit-previous-parts-of-the-pry-buffer-history.md) - [Editing Code In Pry](ruby/editing-code-in-pry.md) - [Encode A String As URL-Safe Base64](ruby/encode-a-string-as-url-safe-base64.md) - [Enumerate A Pairing Of Every Two Sequential Items](ruby/enumerate-a-pairing-of-every-two-sequential-items.md) - [Evaluating One-Off Commands](ruby/evaluating-one-off-commands.md) - [Exclude Values From An Array](ruby/exclude-values-from-an-array.md) - [Execute Several Commands With Backtick Heredoc](ruby/execute-several-commands-with-backtick-heredoc.md) - [Exit A Process With An Error Message](ruby/exit-a-process-with-an-error-message.md) - [Expect A Method To Be Called And Actually Call It](ruby/expect-a-method-to-be-called-and-actually-call-it.md) - [Extract A Column Of Data From A CSV File](ruby/extract-a-column-of-data-from-a-csv-file.md) - [Extract Capture Group Matches With String Slices](ruby/extract-capture-group-matches-with-string-slices.md) - [FactoryGirl Sequences](ruby/factory-girl-sequences.md) - [Fail](ruby/fail.md) - [Fetch Warns About Superseding Block Argument](ruby/fetch-warns-about-superseding-block-argument.md) - [Filter By Type](ruby/filter-by-type.md) - [Find The Min And Max With A Single Call](ruby/find-the-min-and-max-with-a-single-call.md) - [Finding The Source of Ruby Methods](ruby/finding-the-source-of-ruby-methods.md) - [Format A Hash Into A String Template](ruby/format-a-hash-into-a-string-template.md) - [Forward All Arguments To Another Method](ruby/forward-all-arguments-to-another-method.md) - [Gather Positional Arguments In Method Definition](ruby/gather-positional-arguments-in-method-definition.md) - [Generate A Signed JWT Token](ruby/generate-a-signed-jwt-token.md) - [Generate Ruby Version And Gemset Files With RVM](ruby/generate-ruby-version-and-gemset-files-with-rvm.md) - [Get Info About Your RubyGems Environment](ruby/get-info-about-your-ruby-gems-environment.md) - [Get Specific Values From Arrays And Hashes](ruby/get-specific-values-from-hashes-and-arrays.md) - [Get The Names Of The Month](ruby/get-the-names-of-the-month.md) - [Get The Output Of Running A System Program](ruby/get-the-output-of-running-a-system-program.md) - [Get UTC Offset For Different Time Zones](ruby/get-utc-offset-for-different-time-zones.md) - [Identify Outdated Gems](ruby/identify-outdated-gems.md) - [If You Detect None](ruby/if-you-detect-none.md) - [Iterate With An Offset Index](ruby/iterate-with-an-offset-index.md) - [Include Extra Context In A Honeybadger Notify](ruby/include-extra-context-in-a-honeybadger-notify.md) - [Ins And Outs Of Pry](ruby/ins-and-outs-of-pry.md) - [Install And Require Gems Inline Without Gemfile](ruby/install-and-require-gems-inline-without-gemfile.md) - [Install Latest Version Of Ruby With asdf](ruby/install-latest-version-of-ruby-with-asdf.md) - [Invoking Rake Tasks Multiple Times](ruby/invoking-rake-tasks-multiple-times.md) - [IRB Has Built-In Benchmarking With Ruby 3](ruby/irb-has-built-in-benchmarking-with-ruby-3.md) - [Join URI Path Parts](ruby/join-uri-path-parts.md) - [Jump Out Of A Nested Context With Throw/Catch](ruby/jump-out-of-a-nested-context-with-throw-catch.md) - [Last Raised Exception In The Call Stack](ruby/last-raised-exception-in-the-call-stack.md) - [Limit Split](ruby/limit-split.md) - [List The Running Ruby Version](ruby/list-the-running-ruby-version.md) - [Listing Local Variables](ruby/listing-local-variables.md) - [Load A Module And Execute A Statement](ruby/load-a-module-and-execute-a-statement.md) - [Make A Long String Of Text Readable](ruby/make-a-long-string-of-text-readable.md) - [Make An Executable Ruby Script](ruby/make-an-executable-ruby-script.md) - [Make Structs Easier To Use With Keyword Initialization](ruby/make-structs-easier-to-use-with-keyword-initialization.md) - [Map With Index Over An Array](ruby/map-with-index-over-an-array.md) - [Mock Method Chain Calls With RSpec](ruby/mock-method-chain-calls-with-rspec.md) - [Mocking Requests With Partial URIs Using Regex](ruby/mocking-requests-with-partial-uris-using-regex.md) - [Multi-Line Comments](ruby/multi-line-comments.md) - [Named Regex Captures Are Assigned To Variables](ruby/named-regex-captures-are-assigned-to-variables.md) - [Navigate Back In The Browser With Capybara](ruby/navigate-back-in-the-browser-with-capybara.md) - [Next And Previous Floats](ruby/next-and-previous-floats.md) - [OpenStruct Has Bad Performance Characteristics](ruby/open-struct-has-bad-performance-characteristics.md) - [Or Operator Precedence](ruby/or-operator-precedence.md) - [Output Bytecode For A Ruby Program](ruby/output-bytecode-for-a-ruby-program.md) - [Override The Initial Sequence Value](ruby/override-the-initial-sequence-value.md) - [Parallel Bundle Install](ruby/parallel-bundle-install.md) - [Parse JSON Into An OpenStruct](ruby/parse-json-into-an-open-struct.md) - [Parsing A CSV With Quotes In The Data](ruby/parsing-a-csv-with-quotes-in-the-data.md) - [Pass A Block To Count](ruby/pass-a-block-to-count.md) - [Passing Arbitrary Methods As Blocks](ruby/passing-arbitrary-methods-as-blocks.md) - [Passing Arguments To A Rake Task](ruby/passing-arguments-to-a-rake-task.md) - [Pattern Match Values From A Hash](ruby/pattern-match-values-from-a-hash.md) - [Percent Notation](ruby/percent-notation.md) - [Precedence Of Logical Operators](ruby/precedence-of-logical-operators.md) - [Prevent erb_lint From Removing Opening Tags](ruby/prevent-erb-lint-from-removing-opening-tags.md) - [Print Data To Formatted Table](ruby/print-data-to-formatted-table.md) - [Question Mark Operator](ruby/question-mark-operator.md) - [Rake Only Lists Tasks With Descriptions](ruby/rake-only-lists-tasks-with-descriptions.md) - [Read The First Line From A File](ruby/read-the-first-line-from-a-file.md) - [Refer To Implicit Block Argument With It](ruby/refer-to-implicit-block-argument-with-it.md) - [Reference Hash Key With Safe Navigation](ruby/reference-hash-key-with-safe-navigation.md) - [Regenerate Lock File With Newer Bundler](ruby/regenerate-lock-file-with-newer-bundler.md) - [Rendering ERB](ruby/rendering-erb.md) - [Replace The Current Process With An External Command](ruby/replace-the-current-process-with-an-external-command.md) - [Require Entire Gemfile In Pry Session](ruby/require-entire-gemfile-in-pry-session.md) - [Rerun Only Failures With RSpec](ruby/rerun-only-failures-with-rspec.md) - [Retry A Block After An Exception](ruby/retry-a-block-after-an-exception.md) - [Return The Thing Being Printed](ruby/return-the-thing-being-printed.md) - [Returning With Sequel](ruby/returning-with-sequel.md) - [rexml Is A Bundled Gem As Of Ruby 3.0.0](ruby/rexml-is-a-bundled-gem-as-of-ruby-3-0-0.md) - [Run An Older Version Of Bundler](ruby/run-an-older-version-of-bundler.md) - [Running A Single MiniTest Example](ruby/running-a-single-minitest-example.md) - [Safe Navigation Operator](ruby/safe-navigation-operator.md) - [Scripting With RVM](ruby/scripting-with-rvm.md) - [Scroll To Top Of Page With Capybara](ruby/scroll-to-top-of-page-with-capybara.md) - [Search For Gem Versions Available To Install](ruby/search-for-gem-versions-available-to-install.md) - [Set Default Tasks For Rake To Run](ruby/set-default-tasks-for-rake-to-run.md) - [Set RVM Default Ruby](ruby/set-rvm-default-ruby.md) - [Shift The Month On A Date Object](ruby/shift-the-month-on-a-date-object.md) - [Show Public Methods With Pry](ruby/show-public-methods-with-pry.md) - [Show The Bundler Location Of An Installed Gem](ruby/show-the-bundler-location-of-an-installed-gem.md) - [Silence The Output Of A Ruby Statement In Pry](ruby/silence-the-output-of-a-ruby-statement-in-pry.md) - [Single And Double Quoted String Notation](ruby/single-and-double-quoted-string-notation.md) - [Skip Specific CVEs When Auditing Your Bundle](ruby/skip-specific-cves-when-auditing-your-bundle.md) - [Skip The Front Of An Array With Drop](ruby/skip-the-front-of-an-array-with-drop.md) - [Specify Default For Data Definition](ruby/specify-default-for-data-definition.md) - [Specify Dependencies For A Rake Task](ruby/specify-dependencies-for-a-rake-task.md) - [Specify How Random Array#sample Is](ruby/specify-how-random-array-sample-is.md) - [Split A Float Into Its Integer And Decimal](ruby/split-a-float-into-its-integer-and-decimal.md) - [Squeeze Out The Extra Space](ruby/squeeze-out-the-extra-space.md) - [Stack Heredocs In A Method Call](ruby/stack-heredocs-in-a-method-call.md) - [String Interpolation With Instance Variables](ruby/string-interpolation-with-instance-variables.md) - [Summing Collections](ruby/summing-collections.md) - [Triple Equals: The Case Equality Operator](ruby/triple-equals-the-case-equality-operator.md) - [Turn Key And Value Arrays Into A Hash](ruby/turn-key-and-values-arrays-into-a-hash.md) - [Turning Any Class Into An Enumerator](ruby/turning-any-class-into-an-enumerator.md) - [Turning Things Into Hashes](ruby/turning-things-into-hashes.md) - [Uncaught Exceptions In Pry](ruby/uncaught-exceptions-in-pry.md) - [`undef_method` And The Inheritance Hierarchy](ruby/undef-method-and-the-inheritance-hierarchy.md) - [Uninstall Specific Version Of A Ruby Gem](ruby/uninstall-specific-version-of-a-ruby-gem.md) - [Unpacking Strings Into Binary](ruby/unpacking-strings-into-binary.md) - [Up And Down With Integers](ruby/up-and-down-with-integers.md) - [Update The Gemfile Bundled With Version](ruby/update-the-gemfile-bundled-with-version.md) - [Use A Case Statement As A Cond Statement](ruby/use-a-case-statement-as-a-cond-statement.md) - [Use dotenv In A Non-Rails Project](ruby/use-dotenv-in-a-non-rails-project.md) - [Use Tap For Better Test Data Setup](ruby/use-tap-for-better-test-data-setup.md) - [Using BCrypt To Create And Check Hashed Passwords](ruby/using-bcrypt-to-create-and-check-hashed-passwords.md) - [What To Do When You Don't Rescue](ruby/what-to-do-when-you-dont-rescue.md) - [Who Are My Ancestors?](ruby/who-are-my-ancestors.md) - [Wrap Things In An Array, Even Hashes](ruby/wrap-things-in-an-array-even-hashes.md) - [Zero Padding](ruby/zero-padding.md) ### sed - [Apply Multiple Substitutions To The Input](sed/apply-multiple-substitutions-to-the-input.md) - [Equivalence Classes Of Repetition MetaChars](sed/equivalence-classes-of-repetition-metachars.md) - [Extract Value From Command Output With Sed](sed/extract-value-from-command-output-with-sed.md) - [Grab All The Method Names Defined In A Ruby File](sed/grab-all-the-method-names-defined-in-a-ruby-file.md) - [Grab The First Line Of A File](sed/grab-the-first-line-of-a-file.md) - [OSX sed Does Regex A Bit Different](sed/osx-sed-does-regex-a-bit-different.md) - [Output Only Lines Involved In A Substitution](sed/output-only-lines-involved-in-a-substitution.md) - [Reference A Capture In The Regex](sed/reference-a-capture-in-the-regex.md) - [Reference The Full Match In The Replacement](sed/reference-the-full-match-in-the-replacement.md) - [Use An Alternative Delimiter In A Substitution](sed/use-an-alternative-delimiter-in-a-substitution.md) ### Shell - [Check If The First Argument Is Given](shell/check-if-the-first-argument-is-given.md) - [Format And Print The Current Date And Time](shell/format-and-print-the-current-date-and-time.md) ### SQLite - [Display Results In Readable Column Format](sqlite/display-results-in-readable-column-format.md) - [Explore The Database Schema](sqlite/explore-the-database-schema.md) ### Streaming - [Monitor An Audio Input Device In OBS](streaming/monitor-an-audio-input-device-in-obs.md) ### Tailwind CSS - [Apply Tailwind Classes To Existing CSS Class](tailwind/apply-tailwind-classes-to-existing-css-class.md) - [Base Styles For Text Link](tailwind/base-styles-for-text-link.md) - [Disable And Enable A Button](tailwind/disable-and-enable-a-button.md) - [Specify Paths For Purging Unused CSS](tailwind/specify-paths-for-purging-unused-css.md) - [Use Tailwind Typography Prose In Dark Mode](tailwind/use-tailwind-typography-prose-in-dark-mode.md) ### Taskfile - [Create Interactive Picker For Set Of Subtasks](taskfile/create-interactive-picker-for-set-of-subtasks.md) - [Run A Task If It Meets Criteria](taskfile/run-a-task-if-it-meets-criteria.md) ### tmux - [Access Past Copy Buffer History](tmux/access-past-copy-buffer-history.md) - [Add Bindings To Split Panes To Current Directory](tmux/add-bindings-to-split-panes-to-current-directory.md) - [Adjusting Window Pane Size](tmux/adjusting-window-pane-size.md) - [Break Current Pane Out To Separate Window](tmux/break-current-pane-out-to-separate-window.md) - [Change Base Directory Of Existing Session](tmux/change-base-directory-of-existing-session.md) - [Change Base Directory Without Detaching](tmux/change-base-directory-without-detaching.md) - [Change The Default Prefix Key](tmux/change-the-default-prefix-key.md) - [Create A Named tmux Session](tmux/create-a-named-tmux-session.md) - [Create A New Session In A New Server](tmux/create-a-new-session-in-a-new-server.md) - [Cycle Through Layouts](tmux/cycle-through-layouts.md) - [Display Titles For Each Pane In A Window](tmux/display-titles-for-each-pane-in-a-window.md) - [Enabling Vi Mode](tmux/enabling-vi-mode.md) - [Get Mouse Copy/Paste Working In Kitty](tmux/get-mouse-copy-paste-working-in-kitty.md) - [Hiding The Status Bar](tmux/hiding-the-status-bar.md) - [Jumping Between Sessions](tmux/jumping-between-sessions.md) - [Kill All Your tmux Sessions](tmux/kill-all-your-tmux-sessions.md) - [Kill Other Connections To A Session](tmux/kill-other-connections-to-a-session.md) - [Kill The Current Session](tmux/kill-the-current-session.md) - [List All Key Bindings](tmux/list-all-key-bindings.md) - [List Processes Running Across All Session](tmux/list-processes-running-across-all-sessions.md) - [List Sessions](tmux/list-sessions.md) - [Open New Splits To The Current Directory](tmux/open-new-splits-to-the-current-directory.md) - [Open New Window With A Specific Directory](tmux/open-new-window-with-a-specific-directory.md) - [Organizing Windows](tmux/organizing-windows.md) - [Paging Up And Down](tmux/paging-up-and-down.md) - [Pane Killer](tmux/pane-killer.md) - [Reclaiming The Entire Window](tmux/reclaiming-the-entire-window.md) - [Remove The Delay On The Escape Key](tmux/remove-the-delay-on-the-escape-key.md) - [Rename The Current Session](tmux/rename-the-current-session.md) - [Reset An Option Back To Its Default Value](tmux/reset-an-option-back-to-its-default-value.md) - [Set Environment Variables When Creating Session](tmux/set-environment-variables-when-creating-session.md) - [Set Session Specific Environment Variables](tmux/set-session-specific-environment-variables.md) - [Set Up Forwarding Prefix For Nested Session](tmux/set-up-forwarding-prefix-for-nested-session.md) - [Show The Current Value For An Option](tmux/show-the-current-value-for-an-option.md) - [Swap Split Panes](tmux/swap-split-panes.md) - [Switch To A Specific Session And Window](tmux/switch-to-a-specific-session-and-window.md) - [tmux in your tmux](tmux/tmux-in-your-tmux.md) - [Toggle Between Two Common Sessions](tmux/toggle-between-two-common-sessions.md) ### TypeScript - [Add Generic Typing To An Anonymous Function](typescript/add-generic-typing-to-an-anonymous-function.md) - [Add Types To An Object Destructuring](typescript/add-types-to-an-object-destructuring.md) - [Compiler Checks For Unused Params And Variables](typescript/compiler-checks-for-unused-params-and-variables.md) - [Create A Non-Empty Array Type](typescript/create-a-non-empty-array-type.md) - [Create A Union Type From An Array](typescript/create-a-union-type-from-an-array.md) - [Create Union Type From Constants](typescript/create-union-type-from-constants.md) - [Extract Object Type Keys Into A Union Type](typescript/extract-object-type-keys-into-a-union-type.md) - [Extract Object Type Values Into A Union Type](typescript/extract-object-type-values-into-a-union-type.md) - [Generate An Initial tsconfig File](typescript/generate-an-initial-tsconfig-file.md) - [Generate Inferred Type From Zod Schema](typescript/generate-inferred-type-from-zod-schema.md) - [Get The Return Type Of An Async Function](typescript/get-the-return-type-of-an-async-function.md) - [Ignore All Errors In A TypeScript File](typescript/ignore-all-errors-in-a-typescript-file.md) - [Interfaces With The Same Name Are Merged](typescript/interfaces-with-the-same-name-are-merged.md) - [Narrow The Type Of An Array To Its Values](typescript/narrow-the-type-of-an-array-to-its-values.md) - [Re-Export An Imported Type](typescript/re-export-an-imported-type.md) - [Set Path Alias For Cleaner Imports](typescript/set-path-alias-for-cleaner-imports.md) - [Type Narrowing With Const VS Let Strings](typescript/type-narrowing-with-const-vs-let-strings.md) - [Type Narrowing With Similarly Shaped Objects](typescript/type-narrowing-with-similarly-shaped-objects.md) - [Type Promise Results With The Awaited Type](typescript/type-promise-results-with-the-awaited-type.md) - [Use An Array Check For Type Narrowing](typescript/use-an-array-check-for-type-narrowing.md) - [Zero-Config Environments For Trying Out Types](typescript/zero-config-environments-for-trying-out-types.md) ### Unix - [All The Environment Variables](unix/all-the-environment-variables.md) - [Apply Successive Filters To Lines In Less](unix/apply-successive-filters-to-lines-in-less.md) - [Authorize A cURL Request](unix/authorize-a-curl-request.md) - [Cat A File With Line Numbers](unix/cat-a-file-with-line-numbers.md) - [Cat Files With Color Using Bat](unix/cat-files-with-color-using-bat.md) - [Change Default Shell For A User](unix/change-default-shell-for-a-user.md) - [Change To That New Directory](unix/change-to-that-new-directory.md) - [Check Connected Stripe Account Name](unix/check-connected-stripe-account-name.md) - [Check If A Port Is In Use](unix/check-if-a-port-is-in-use.md) - [Check If Command Is Executable Before Using](unix/check-if-command-is-executable-before-using.md) - [Check SSH Key Fingerprints Of Known Hosts](unix/check-ssh-key-fingerprints-of-known-hosts.md) - [Check The Current Working Directory](unix/check-the-current-working-directory.md) - [Check The Installed OpenSSL Version](unix/check-the-installed-openssl-version.md) - [Clear The Screen](unix/clear-the-screen.md) - [Combine All My TILs Into A Single File](unix/combine-all-my-tils-into-a-single-file.md) - [Command Line Length Limitations](unix/command-line-length-limitations.md) - [Compare Two Variables In A Bash Script](unix/compare-two-variables-in-a-bash-script.md) - [Configure cd To Behave Like pushd In Zsh](unix/configure-cd-to-behave-like-pushd-in-zsh.md) - [Convert JPEG To PNG With ffmpeg](unix/convert-jpeg-to-png-with-ffmpeg.md) - [Convert SVG To Favicon](unix/convert-svg-to-favicon.md) - [Copying File Contents To System Paste Buffer](unix/copying-file-contents-to-system-paste-buffer.md) - [Copying Nested Directories With Ditto](unix/copying-nested-directories-with-ditto.md) - [Count The Lines In A CSV Where A Column Is Empty](unix/count-the-lines-in-a-csv-where-a-column-is-empty.md) - [Count The Number Of Matches In A Grep](unix/count-the-number-of-matches-in-a-grep.md) - [Count The Number Of ripgrep Pattern Matches](unix/count-the-number-of-ripgrep-pattern-matches.md) - [Count The Number Of Words On A Webpage](unix/count-the-number-of-words-on-a-webpage.md) - [Create A File Descriptor with Process Substitution](unix/create-a-file-descriptor-with-process-substitution.md) - [Create A Filename With The Current Date](unix/create-a-filename-with-the-current-date.md) - [Create A Sequence Of Values With A Step](unix/create-a-sequence-of-values-with-a-step.md) - [Curl With Cookies](unix/curl-with-cookies.md) - [Curling For Headers](unix/curling-for-headers.md) - [Curling With Basic Auth Credentials](unix/curling-with-basic-auth-credentials.md) - [Determine ipv4 And ipv6 Public IP Addresses](unix/determine-ipv4-and-ipv6-public-ip-addresses.md) - [Diff Two Files In Unified Format](unix/diff-two-files-in-unified-format.md) - [Different Ways To Generate A v4 UUID](unix/different-ways-to-generate-a-v4-uuid.md) - [Display All The Terminal Colors](unix/display-all-the-terminal-colors.md) - [Display Free Disk Space](unix/display-free-disk-space.md) - [Display Line Numbers While Using Less](unix/display-line-numbers-while-using-less.md) - [Display The Contents Of A Directory As A Tree](unix/display-the-contents-of-a-directory-as-a-tree.md) - [Do A Dry Run Of An rsync](unix/do-a-dry-run-of-an-rsync.md) - [Do Not Overwrite Existing Files](unix/do-not-overwrite-existing-files.md) - [Download A File With Curl](unix/download-a-file-with-curl.md) - [Enable Multi-Select Of Results With fzf](unix/enable-multi-select-of-results-with-fzf.md) - [Exclude A Command From The ZSH History File](unix/exclude-a-command-from-the-zsh-history-file.md) - [Exclude A Directory With Find](unix/exclude-a-directory-with-find.md) - [Exclude A Specific File From fd Results](unix/exclude-a-specific-file-from-fd-results.md) - [Exclude Certain Files From An rsync Run](unix/exclude-certain-files-from-an-rsync-run.md) - [Figure Out The Week Of The Year From The Terminal](unix/figure-out-the-week-of-the-year-from-the-terminal.md) - [File Type Info With File](unix/file-type-info-with-file.md) - [Find All Files Matching A Name With fd](unix/find-all-files-matching-a-name-with-fd.md) - [Find All Files With A Specific Extension With fd](unix/find-all-files-with-a-specific-extension-with-fd.md) - [Find All Tool Version Files Containing Postgres](unix/find-all-tool-version-files-containing-postgres.md) - [Find And Copy A Value From Large JSON Output](unix/find-and-copy-a-value-from-large-json-output.md) - [Find Any Dotfiles That Modify Path Env Var](unix/find-any-dotfiles-that-modify-path-env-var.md) - [Find A File Installed By Brew](unix/find-a-file-installed-by-brew.md) - [Find Duplicate Lines In A File](unix/find-duplicate-lines-in-a-file.md) - [Find Files With fd](unix/find-files-with-fd.md) - [Find Newer Files](unix/find-newer-files.md) - [Find Occurrences Of Multiple Values With Ripgrep](unix/find-occurrences-of-multiple-values-with-ripgrep.md) - [Find Top-Level Directories Matching A Pattern](unix/find-top-level-directories-matching-a-pattern.md) - [Fix Previous Command With fc](unix/fix-previous-command-with-fc.md) - [Fix Shim Path After asdf Upgrade](unix/fix-shim-path-after-asdf-upgrade.md) - [Fix Unlinked Node Binaries With asdf](unix/fix-unlinked-node-binaries-with-asdf.md) - [Format And Display Small Amounts Of Columnar Data](unix/format-and-display-small-amounts-of-columnar-data.md) - [Forward Multiple Ports Over SSH](unix/forward-multiple-ports-over-ssh.md) - [Generate A SAML Key And Certificate Pair](unix/generate-a-saml-key-and-certificate-pair.md) - [Generate A Sequence Of Numbered Items](unix/generate-a-sequence-of-numbered-items.md) - [Generate Base64 Encoding Without Newlines](unix/generate-base64-encoding-without-newlines.md) - [Generate Random 20-Character Hex String](unix/generate-random-20-character-hex-string.md) - [Get A List Of Locales On Your System](unix/get-a-list-of-locales-on-your-system.md) - [Get Matching Filenames As Output From Grep](unix/get-matching-filenames-as-output-from-grep.md) - [Get The SHA256 Hash For A File](unix/get-the-sha256-hash-for-a-file.md) - [Get The Unix Timestamp](unix/get-the-unix-timestamp.md) - [Get Word Count For All Files In Git Repo](unix/get-word-count-for-all-files-in-git-repo.md) - [Global Substitution On The Previous Command](unix/global-substitution-on-the-previous-command.md) - [Globbing For All Directories In Zsh](unix/globbing-for-all-directories-in-zsh.md) - [Globbing For Filenames In Zsh](unix/globbing-for-filenames-in-zsh.md) - [Gracefully Exit A Script With Trap](unix/gracefully-exit-a-script-with-trap.md) - [Grep For Files Without A Match](unix/grep-for-files-without-a-match.md) - [Grep For Files With Multiple Matches](unix/grep-for-files-with-multiple-matches.md) - [Grep For Multiple Patterns](unix/grep-for-multiple-patterns.md) - [Have Script ShellCheck Itself When Executing](unix/have-script-shellcheck-itself-when-executing.md) - [Hexdump A Compiled File](unix/hexdump-a-compiled-file.md) - [Ignore A Directory During ripgrep Search](unix/ignore-a-directory-during-ripgrep-search.md) - [Ignore The Alias When Running A Command](unix/ignore-the-alias-when-running-a-command.md) - [Include Ignore Files In Ripgrep Search](unix/include-ignore-files-in-ripgrep-search.md) - [Inspect EXIF Data For An Image File](unix/inspect-exif-data-for-an-image-file.md) - [Interactively Browse Available Node Versions](unix/interactively-browse-availabile-node-versions.md) - [Interactively Switch asdf Package Versions](unix/interactively-switch-asdf-package-versions.md) - [Interpret Cron Schedule From The CLI](unix/interpret-cron-schedule-from-the-cli.md) - [Jump To The Ends Of Your Shell History](unix/jump-to-the-ends-of-your-shell-history.md) - [Kill Everything Running On A Certain Port](unix/kill-everything-running-on-a-certain-port.md) - [Killing A Frozen SSH Session](unix/killing-a-frozen-ssh-session.md) - [Last Argument Of The Last Command](unix/last-argument-of-the-last-command.md) - [Less With Style](unix/less-with-style.md) - [Limit Protocols Used In A cURL Command](unix/limit-protocols-used-in-a-curl-command.md) - [List All Fonts On Your Machine](unix/list-all-fonts-on-your-machine.md) - [List All The Enabled ZSH Options](unix/list-all-the-enabled-zsh-options.md) - [List All Users](unix/list-all-users.md) - [List Files In A Single Column](unix/list-files-in-a-single-column.md) - [List Files Ordered By Modification Date](unix/list-files-ordered-by-modification-date.md) - [List Names Of Files With Matches](unix/list-names-of-files-with-matches.md) - [List Of Sessions To A Machine](unix/list-of-sessions-to-a-machine.md) - [List Parent pid With ps](unix/list-parent-pid-with-ps.md) - [List Stats For A File](unix/list-stats-for-a-file.md) - [List The Available JDKs](unix/list-the-available-jdks.md) - [List The PID And Name Of Current Shell Process](unix/list-the-pid-and-name-of-current-shell-process.md) - [List The Stack Of Remembered Directories](unix/list-the-stack-of-remembered-directories.md) - [List TXT DNS Records For A Domain](unix/list-txt-dns-records-for-a-domain.md) - [Load Env Vars In Bash Script](unix/load-env-vars-in-bash-script.md) - [Look Through All Files That Have Been Git Stashed](unix/look-through-all-files-that-have-been-git-stashed.md) - [Make Direnv Less Noisy](unix/make-direnv-less-noisy.md) - [Make Neovim The Default Way To View Man Pages](unix/make-neovim-the-default-way-to-view-man-pages.md) - [Manually Pass Two Git Files To Delta](unix/manually-pass-two-git-files-to-delta.md) - [Map A Domain To localhost](unix/map-a-domain-to-localhost.md) - [Negative Look-Ahead Search With ripgrep](unix/negative-look-ahead-search-with-ripgrep.md) - [Occupy A Local Port With Netcat](unix/occupy-a-local-port-with-netcat.md) - [Only Show The Matches](unix/only-show-the-matches.md) - [Open The Current Command In An Editor](unix/open-the-current-command-in-an-editor.md) - [Output The Last N Bytes Of A Large File](unix/output-the-last-n-bytes-of-a-large-file.md) - [Partial String Matching In Bash Scripts](unix/partial-string-matching-in-bash-scripts.md) - [PID Of The Current Shell](unix/pid-of-the-current-shell.md) - [Print A Range Of Lines For A File With Bat](unix/print-a-range-of-lines-for-a-file-with-bat.md) - [Print DateTime Represented By Unix Timestamp](unix/print-datetime-represented-by-unix-timestamp.md) - [Print Milliseconds In Human-Readable Format](unix/print-milliseconds-in-human-readable-format.md) - [Print Out Files In Reverse](unix/print-out-files-in-reverse.md) - [Print The Current Date In Human-Readable Format](unix/print-the-current-date-in-human-readable-format.md) - [Produce A Lowercase V4 UUID](unix/produce-a-lowercase-v4-uuid.md) - [Provide A Fallback Value For Unset Parameter](unix/provide-a-fallback-value-for-unset-parameter.md) - [Remove A Directory Called `-p`](unix/remove-a-directory-called-dash-p.md) - [Rename A Bunch Of Files By Constructing mv Commands](unix/rename-a-bunch-of-files-by-constructing-mv-commands.md) - [Repeat Yourself](unix/repeat-yourself.md) - [Replace Pattern Across Many Files In A Project](unix/replace-pattern-across-many-files-in-a-project.md) - [Run A Command Repeatedly Several Times](unix/run-a-command-repeatedly-several-times.md) - [Run A cURL Command Without The Progress Meter](unix/run-a-curl-command-without-the-progress-meter.md) - [Safely Edit The Sudoers File With Vim](unix/safely-edit-the-sudoers-file-with-vim.md) - [Saying Yes](unix/saying-yes.md) - [Search Files Specific To A Language](unix/search-files-specific-to-a-language.md) - [Search For Homebrew Packages To Install](unix/search-for-homebrew-packages-to-install.md) - [Search History](unix/search-history.md) - [Search Man Page Descriptions](unix/search-man-page-descriptions.md) - [Securely Remove Files](unix/securely-remove-files.md) - [See Where asdf Gets Current Tool Version](unix/see-where-asdf-gets-current-tool-version.md) - [Set The asdf Package Version For A Single Shell](unix/set-the-asdf-package-version-for-a-single-shell.md) - [Shorten SSH Commands With Aliases](unix/shorten-ssh-commands-with-aliases.md) - [Show A File Preview When Searching With FZF](unix/show-a-file-preview-when-searching-with-fzf.md) - [Show Disk Usage For The Current Directory](unix/show-disk-usage-for-the-current-directory.md) - [Show The Size Of Everything In A Directory](unix/show-the-size-of-everything-in-a-directory.md) - [Show Tree View Of Processes And Subprocesses](unix/show-tree-view-of-processes-and-subprocesses.md) - [Skip Paging If Output Fits On Screen With Less](unix/skip-paging-if-output-fits-on-screen-with-less.md) - [SSH Escape Sequences](unix/ssh-escape-sequences.md) - [SSH With Port Forwarding](unix/ssh-with-port-forwarding.md) - [Specify The Language For A File With Bat](unix/specify-the-language-for-a-file-with-bat.md) - [Sort In Numerical Order](unix/sort-in-numerical-order.md) - [Switch Versions of a Brew Formula](unix/switch-versions-of-a-brew-formula.md) - [Tell direnv To Load The Env File](unix/tell-direnv-to-load-the-env-file.md) - [Touch Access And Modify Times Individually](unix/touch-access-and-modify-times-individually.md) - [Transform Text To Lowercase](unix/transform-text-to-lowercase.md) - [Type Fewer Paths With Brace Expansion](unix/type-fewer-paths-with-brace-expansion.md) - [Undo Changes Made To Current Terminal Prompt](unix/undo-changes-made-to-current-terminal-prompt.md) - [Undo Some Command Line Editing](unix/undo-some-command-line-editing.md) - [Unrestrict Where ripgrep Searches](unix/unrestrict-where-ripgrep-searches.md) - [Update Package Versions Known By asdf Plugin](unix/update-package-versions-known-by-asdf-plugin.md) - [Use fzf To Change Directories](unix/use-fzf-to-change-directories.md) - [Use Negative Lookbehind Matching With ripgrep](unix/use-negative-lookbehind-matching-with-ripgrep.md) - [Use Regex Pattern Matching With Grep](unix/use-regex-pattern-matching-with-grep.md) - [View A Web Page In The Terminal](unix/view-a-web-page-in-the-terminal.md) - [View The Source For A Brew Formula](unix/view-the-source-for-a-brew-formula.md) - [Watch The Difference](unix/watch-the-difference.md) - [Watch This Run Repeatedly](unix/watch-this-run-repeatedly.md) - [Where Are The Binaries?](unix/where-are-the-binaries.md) - [xargs Default Command Is echo](unix/xargs-default-command-is-echo.md) - [xargs Ignores Alias Substitution By Default](unix/xargs-ignores-alias-substitution-by-default.md) ### Vercel - [Add Web Server Layer Redirects](vercel/add-web-server-layer-redirects.md) - [Deploy An App Without Pushing An Empty Commit](vercel/deploy-an-app-without-pushing-an-empty-commit.md) - [Naming Of The Vercel Config File](vercel/naming-of-the-vercel-config-file.md) - [Pin Specific pnpm Version For Builds](vercel/pin-specific-pnpm-version-for-builds.md) - [Share Development Environment Variables Via CLI](vercel/share-development-environment-variables-via-cli.md) ### Vim - [Aborting Git Commits And Rebases](vim/aborting-git-commits-and-rebases.md) - [Absolute And Relative Line Numbers](vim/absolute-and-relative-line-numbers.md) - [Add A File Without Loading It](vim/add-a-file-without-loading-it.md) - [Add Custom Dictionary Words](vim/add-custom-dictionary-words.md) - [All The Ways To Write And Quit In Vim](vim/all-the-ways-to-write-and-quit-in-vim.md) - [Almost The End Of The Line](vim/almost-the-end-of-the-line.md) - [Alternate Files With vim-rails](vim/alternate-files-with-vim-rails.md) - [Always Keep The Gutter Open](vim/always-keep-the-gutter-open.md) - [Amend Commits With Fugitive](vim/amend-commits-with-fugitive.md) - [Backspace Options](vim/backspace-options.md) - [Beginning And End Of Previous Change](vim/beginning-and-end-of-previous-change.md) - [The Black Hole Register](vim/the-black-hole-register.md) - [Blank Lines Above And Below](vim/blank-lines-above-and-below.md) - [Breaking The Undo Sequence](vim/breaking-the-undo-sequence.md) - [Buffer Time Travel](vim/buffer-time-travel.md) - [Build And Install A Go Program](vim/build-and-install-a-go-program.md) - [Bypass On-Save Tooling When Writing File](vim/bypass-on-save-tooling-when-writing-file.md) - [Case-Aware Substitution With vim-abolish](vim/case-aware-substitution-with-vim-abolish.md) - [Case-Insensitive Substitution](vim/case-insensitive-substitution.md) - [Center The Cursor](vim/center-the-cursor.md) - [Check For An Executable](vim/check-for-an-executable.md) - [Check Your Current Color Scheme](vim/check-your-current-color-scheme.md) - [Clear Out The Jump List](vim/clear-out-the-jump-list.md) - [Close All Other Splits](vim/close-all-other-splits.md) - [Close All Other Windows](vim/close-all-other-windows.md) - [Close the Current Buffer](vim/close-the-current-buffer.md) - [Coerce The Current Filetype](vim/coerce-the-current-filetype.md) - [Coercing Casing With vim-abolish](vim/coercing-casing-with-vim-abolish.md) - [Configure FZF To Use fd For File Finding](vim/configure-fzf-to-use-fd-for-file-finding.md) - [Count the Number of Matches](vim/count-the-number-of-matches.md) - [Create A New Directory In netrw](vim/create-a-new-directory-in-netrw.md) - [Create A New File In A New Directory](vim/create-a-new-file-in-a-new-directory.md) - [Creating Non-Existent Directories](vim/creating-non-existent-directories.md) - [Default netrw To Tree Liststyle](vim/default-netrw-to-tree-liststyle.md) - [Delete Every Other Line](vim/delete-every-other-line.md) - [Delete Lines That Match A Pattern](vim/delete-lines-that-match-a-pattern.md) - [Delete To The End Of The Line](vim/delete-to-the-end-of-the-line.md) - [Deleting Buffers In BufExplorer](vim/deleting-buffers-in-bufexplorer.md) - [Deleting Directories Of Files From netrw](vim/deleting-directories-of-files-from-netrw.md) - [Detect If You Are On A Mac](vim/detect-if-you-are-on-a-mac.md) - [Difference Between :wq and :x](vim/difference-between-wq-and-x.md) - [Display Word Count Stats](vim/display-word-count-stats.md) - [Edges Of The Selection](vim/edges-of-the-selection.md) - [Edit A File At A Specific Line Number](vim/edit-a-file-at-a-specific-line-number.md) - [Edit A File Starting On The Last Line](vim/edit-a-file-starting-on-the-last-line.md) - [End Of The Word](vim/end-of-the-word.md) - [Escaping Terminal-Mode In An Nvim Terminal](vim/escaping-terminal-mode-in-an-nvim-terminal.md) - [Filter Lines Through An External Program](vim/filter-lines-through-an-external-program.md) - [Find The Nth Character Position In A File](vim/find-the-nth-character-position-in-a-file.md) - [Fix The Spelling Of A Word](vim/fix-the-spelling-of-a-word.md) - [Fold A Visual Selection And Expand It Back](vim/fold-a-visual-selection-and-expand-it-back.md) - [For When That Escape Key Is Hard To Reach](vim/for-when-that-escape-key-is-hard-to-reach.md) - [Format Long Lines To Text Width](vim/format-long-lines-to-text-width.md) - [From Ruby Variables To JavaScript Variables](vim/from-ruby-variables-to-javascript-variables.md) - [Generate and Edit Rails Migration](vim/generate-and-edit-rails-migration.md) - [Get The pid Of The Session](vim/get-the-pid-of-the-session.md) - [Go Back To The Previous Window](vim/go-back-to-the-previous-window.md) - [Go To Beginning And End Of Line](vim/go-to-beginning-and-end-of-line.md) - [Go To File With Line Number](vim/go-to-file-with-line-number.md) - [Grepping Through The Vim Help Files](vim/grepping-through-the-vim-help-files.md) - [Head of File Name](vim/head-of-file-name.md) - [Help For Non-Normal Mode Features](vim/help-for-non-normal-mode-features.md) - [Highlighting Search Matches](vim/highlighting-search-matches.md) - [Horizontal to Vertical and Back Again](vim/horizontal-to-vertical-and-back-again.md) - [Increment All The Numbers](vim/increment-all-the-numbers.md) - [Incremental Searching](vim/incremental-searching.md) - [Interact With The Alternate File](vim/interact-with-the-alternate-file.md) - [Interactive Buffer List](vim/interactive-buffer-list.md) - [Joining Lines Together](vim/joining-lines-together.md) - [Jump Back To The Latest Jump Position](vim/jump-back-to-the-latest-jump-position.md) - [Jump Between And Stage Git Hunks With Fugitive](vim/jump-between-and-stage-git-hunks-with-fugitive.md) - [Jump To Matching Pair](vim/jump-to-matching-pair.md) - [Jump To The Next Misspelling](vim/jump-to-the-next-misspelling.md) - [List All Buffers](vim/list-all-buffers.md) - [List autocmds Configured For The Current Buffer](vim/list-autocmds-configured-for-the-current-buffer.md) - [List Of Plugins](vim/list-of-plugins.md) - [Load A Directory Of Files Into The Buffer List](vim/load-a-directory-of-files-into-the-buffer-list.md) - [Make Directories For The Current File](vim/make-directories-for-the-current-file.md) - [Marks Across Vim Sessions](vim/marks-across-vim-sessions.md) - [Match The Beginning And End Of Words](vim/match-the-beginning-and-end-of-words.md) - [Moving To A Specific Line](vim/moving-to-a-specific-line.md) - [Navigate To The Nth Column On A Line](vim/navigate-to-the-nth-column-on-a-line.md) - [Navigating By Blank Lines](vim/navigating-by-blank-lines.md) - [NETRW Listing Styles](vim/netrw-listing-styles.md) - [Next Modified Buffer](vim/next-modified-buffer.md) - [Normal Node Binding To Just Quit](vim/normal-mode-binding-to-just-quit.md) - [Open A Tag In A Split Window](vim/open-a-tag-in-a-split-window.md) - [Open an Unnamed Buffer](vim/open-an-unnamed-buffer.md) - [Open FZF Result In A Split](vim/open-fzf-result-in-a-split.md) - [Open Routes File With vim-rails](vim/open-routes-file-with-vim-rails.md) - [Open The Directory Of The Current File](vim/open-the-directory-of-the-current-file.md) - [Open The Fugitive Git Summary Window](vim/open-the-fugitive-git-summary-window.md) - [Open The Gemfile](vim/open-the-gemfile.md) - [Open The Latest Rails Migration](vim/open-the-latest-rails-migration.md) - [Open The Selected Lines In GitHub With Gbrowse](vim/open-the-selected-lines-in-github-with-gbrowse.md) - [Open Vim To A Tag Definition](vim/open-vim-to-a-tag-definition.md) - [Opening a URL](vim/opening-a-url.md) - [Opening Man Pages In Vim](vim/opening-man-pages-in-vim.md) - [Paste A Register From Insert Mode](vim/paste-a-register-from-insert-mode.md) - [Preventing Typos with Abbreviations](vim/preventing-typos-with-abbreviations.md) - [Previous Buffer](vim/previous-buffer.md) - [Previous Visual Selection](vim/previous-visual-selection.md) - [Print The Relative Path Of The Current File](vim/print-the-relative-path-of-the-current-file.md) - [Print Version Information](vim/print-version-information.md) - [Quick File Info](vim/quick-file-info.md) - [Quick Man Pages](vim/quick-man-pages.md) - [Quick Quickfix List Navigation](vim/quick-quickfix-list-navigation.md) - [Quickly Fix A Misspelled Word](vim/quickly-fix-a-misspelled-word.md) - [Quickly Switch To A Buffer By Number](vim/quickly-switch-to-a-buffer-by-number.md) - [Quit When There Is An Argument List](vim/quit-when-there-is-an-argument-list.md) - [Re-indenting Your Code](vim/reindenting-your-code.md) - [Read In The Contents Of A Rails File](vim/read-in-the-contents-of-a-rails-file.md) - [Rename A File Through netrw](vim/rename-a-file-through-netrw.md) - [Rename Current File](vim/rename-current-file.md) - [Repeat The Previous Change](vim/repeat-the-previous-change.md) - [Repeating Characters](vim/repeating-characters.md) - [Replace A Character](vim/replace-a-character.md) - [Reset Target tslime Pane](vim/reset-target-tslime-pane.md) - [Reverse A Group Of Lines](vim/reverse-a-group-of-lines.md) - [Reword A Commit Message With Fugitive](vim/reword-a-commit-message-with-fugitive.md) - [Rotate Everything By 13 Letters](vim/rotate-everything-by-13-letters.md) - [Rotate The Orientation Of Split Windows](vim/rotate-the-orientation-of-split-windows.md) - [Running Bundle With vim-bundler](vim/running-bundle-with-vim-bundler.md) - [Scrolling Relative to the Cursor](vim/scrolling-relative-to-the-cursor.md) - [Search Backward Through A File](vim/search-backward-through-a-file.md) - [Searching For Hex Digits](vim/searching-for-hex-digits.md) - [Select Several Results From An FZF Search](vim/select-several-results-from-an-fzf-search.md) - [Set End Of Line Markers](vim/set-end-of-line-markers.md) - [Set Your Color Scheme](vim/set-your-color-scheme.md) - [Setting Filetype With Modelines](vim/setting-filetype-with-modelines.md) - [Show All Syntax Highlighting Rules](vim/show-all-syntax-highlighting-rules.md) - [Show Matching Entries For Help](vim/show-matching-entries-for-help.md) - [Specify The Line Height Of The Quick Fix Window](vim/specify-the-line-height-of-the-quick-fix-window.md) - [Split Different](vim/split-different.md) - [Split The Current Window](vim/split-the-current-window.md) - [Splitting For New Files](vim/splitting-for-new-files.md) - [Source Original vimrc When Using Neovim](vim/source-original-vimrc-when-using-neovim.md) - [Sum A Bunch Of Numbers In The Current File](vim/sum-a-bunch-of-numbers-in-the-current-file.md) - [Swap Occurrences Of Two Words](vim/swap-occurrences-of-two-words.md) - [Swapping Split Windows](vim/swapping-split-windows.md) - [Swap The Position Of Two Split Windows](vim/swap-the-position-of-two-split-windows.md) - [Switch Moving End Of Visual Selection](vim/switch-moving-end-of-visual-selection.md) - [Tabs To Spaces](vim/tabs-to-spaces.md) - [The Vim Info File](vim/the-vim-info-file.md) - [Toggle Absolute And Relative Paths In BufExplorer](vim/toggle-absolute-and-relative-paths-in-bufexplorer.md) - [Toggling Syntax Highlighting](vim/toggling-syntax-highlighting.md) - [Turning Off Search Highlighting](vim/turning-off-search-highlighting.md) - [Unloading A Buffer](vim/unloading-a-buffer.md) - [Use Active Window With BufExplorer](vim/use-active-window-with-bufexplorer.md) - [Use The Terminal Inside A Vim Session](vim/use-the-terminal-inside-a-vim-session.md) - [Using vim-surround With A Visual Selection](vim/using-vim-surround-with-a-visual-selection.md) - [Verbose Commits With Fugitive](vim/verbose-commits-with-fugitive.md) - [View Commit History of a File](vim/view-commit-history-of-a-file.md) - [View The Current File In GitHub](vim/view-the-current-file-in-github.md) - [Viewing Man Pages with man.vim](vim/viewing-man-pages-with-man-vim.md) - [Vim Without The Extras](vim/vim-without-the-extras.md) - [What Is On The Runtime Path?](vim/what-is-on-the-runtime-path.md) - [Whole Line Auto-Completion](vim/whole-line-auto-completion.md) - [Wrap With Some Room](vim/wrap-with-some-room.md) ### VSCode - [Add The VSCode CLI To Your Path](vscode/add-the-vscode-cli-to-your-path.md) - [Advance Through Search Results](vscode/advance-through-search-results.md) - [Enable Breadcrumbs For Version 1.26 Release](vscode/enable-breadcrumbs-for-version-126-release.md) - [Find The Location Of User Settings JSON File](vscode/find-the-location-of-user-settings-json-file.md) - [Jump To Problems In The Current File](vscode/jump-to-problems-in-the-current-file.md) - [Open An Integrated Terminal Window](vscode/open-an-integrated-terminal-window.md) - [Open File On Remote Like GitHub](vscode/open-file-on-remote-like-github.md) - [Pop Open The Quick Fix Window](vscode/pop-open-the-quick-fix-window.md) - [Step Through Project-Wide Search Results](vscode/step-through-project-wide-search-results.md) - [Synchronize Vim Clipboard With System Clipboard](vscode/synchronize-vim-clipboard-with-system-clipboard.md) - [Toggle Between Terminals](vscode/toggle-between-terminals.md) - [Turn Off Display Of Tabs For Files](vscode/turn-off-display-of-tabs-for-files.md) ### Webpack - [Better Module Imports With Aliases](webpack/better-module-imports-with-aliases.md) - [Debugging With Full Source Maps](webpack/debugging-with-full-source-maps.md) - [Run ESLint As A Preloader](webpack/run-eslint-as-a-preloader.md) - [Specify Port Of CRA's Webpack Dev Server](webpack/specify-port-of-cra-webpack-dev-server.md) - [Use A Specific Config File](webpack/use-a-specific-config-file.md) ### Workflow - [Add Hotkeys For Specific Raycast Extensions](workflow/add-hotkeys-for-specific-raycast-extensions.md) - [Add Subscriber To Kit Form Via API](workflow/add-subscriber-to-kit-form-via-api.md) - [Add Subtitles To Existing Mux Video Asset](workflow/add-subtitles-to-existing-mux-video-asset.md) - [Access 1Password Credential From CLI](workflow/access-1password-credential-from-cli.md) - [Allow Key-Repeating With Cursor](workflow/allow-key-repeating-with-cursor.md) - [Break Justfile Into Separate Hidden Steps](workflow/break-justfile-into-separate-hidden-steps.md) - [Change Window Name In iTerm](workflow/change-window-name-in-iterm.md) - [Configure Email Redirect With Cloudflare](workflow/configure-email-redirect-with-cloudflare.md) - [Control Media With Drop Keyboard](workflow/control-media-with-drop-keyboard.md) - [Convert An ePub Document To PDF On Mac](workflow/convert-an-epub-document-to-pdf-on-mac.md) - [Create A Local Sanity Dataset Backup](workflow/create-a-local-sanity-dataset-backup.md) - [Create A Public URL For A Local Server](workflow/create-a-public-url-for-a-local-server.md) - [Create Todo Items In Logseq](workflow/create-todo-items-in-logseq.md) - [Do Project Time Tracking From The CLI](workflow/do-project-time-tracking-from-the-cli.md) - [Enable Dev Tools For Safari](workflow/enable-dev-tools-for-safari.md) - [Forward Stripe Events To Local Server](workflow/forward-stripe-events-to-local-server.md) - [Get URL For GitHub User Profile Photo](workflow/get-url-for-github-user-profile-photo.md) - [Get Your Public IP Address](workflow/get-your-public-ip-address.md) - [Import A Github Project Into CodeSandbox](workflow/import-a-github-project-into-codesandbox.md) - [Interactively Kill A Process With fkill](workflow/interactively-kill-a-process-with-fkill.md) - [Open Slack's Keyboard Shortcuts Reference Panel](workflow/open-slacks-keyboard-shortcuts-reference-panel.md) - [Pop Videos Out As Picture-in-Picture](workflow/pop-videos-out-as-picture-in-picture.md) - [Prune The Excess From node_modules](workflow/prune-the-excess-from-node-modules.md) - [Rotate An Image To Be Oriented Upright](workflow/rotate-an-image-to-be-oriented-upright.md) - [See Overlaps For A Set Of Time Zones](workflow/see-overlaps-for-a-set-of-time-zones.md) - [Send A Message To A Discord Channel](workflow/send-a-message-to-a-discord-channel.md) - [Send A PDF To Your Kindle](workflow/send-a-pdf-to-your-kindle.md) - [Set Recurring Reminders In Slack](workflow/set-recurring-reminders-in-slack.md) - [Show Linting Errors In Zed](workflow/show-linting-errors-in-zed.md) - [Temporarily Hide CleanShot X Capture Previews](workflow/temporarily-hide-cleanshot-x-capture-previews.md) - [Toggle Between Stories In Storybook](workflow/toggle-between-stories-in-storybook.md) - [Update asdf Plugins With Latest Package Versions](workflow/update-asdf-plugins-with-latest-package-versions.md) - [View A Nicely-Formatted CSV In Terminal](workflow/view-a-nicely-formatted-csv-in-terminal.md) - [View The PR For The Current GitHub Branch](workflow/view-the-pr-for-the-current-github-branch.md) ### XState - [Always Use Inline Functions With Assign](xstate/always-use-inline-functions-with-assign.md) - [Custom Jest Matcher For XState Machine States](xstate/custom-jest-matcher-for-xstate-machine-states.md) - [Define Event That Does Internal Self Transition](xstate/define-event-that-does-internal-self-transition.md) - [Events Stop Propagating Once Handled](xstate/events-stop-propagating-once-handled.md) - [Inline Actions vs Actions In Machine Options](xstate/inline-actions-vs-actions-in-machine-options.md) - [Make Immediate And Delayed Transitions](xstate/make-immediate-and-delayed-transitions.md) - [Simple States And Composite States](xstate/simple-states-and-composite-states.md) - [Start A Machine In A Specific State](xstate/start-a-machine-in-a-specific-state.md) - [Use An XState Machine With React](xstate/use-an-xstate-machine-with-react.md) ### YAML - [Create Multi-Line Strings Without The Line Breaks](yaml/create-multi-line-strings-without-the-line-breaks.md) - [YAML Is A Superset Of JSON](yaml/yaml-is-a-superset-of-json.md) ### Zod - [Check If An Object Is Empty With Zod](zod/check-if-an-object-is-empty-with-zod.md) - [Create A Schema That Matches On Any Object](zod/create-a-schema-that-matches-on-any-object.md) - [Create Union Type Of Nearly Identical Objects](zod/create-union-type-of-nearly-identical-objects.md) - [Get Readable Errors From Schema Parse](zod/get-readable-errors-from-schema-parse.md) - [Incorporate Existing Type Into Zod Schema](zod/incorporate-existing-type-into-zod-schema.md) - [Set Custom Error Message For Nonempty Array](zod/set-custom-error-message-for-nonempty-array.md) ### Zsh - [A Better Way To Reload ZSH Configuration](zsh/a-better-way-to-reload-zsh-configuration.md) - [Add To The Path Via Path Array](zsh/add-to-the-path-via-path-array.md) - [Create And Jump Into A Directory](zsh/create-and-jump-into-a-directory.md) - [Link A Scalar To An Array](zsh/link-a-scalar-to-an-array.md) - [Use A Space To Exclude Command From History](zsh/use-a-space-to-exclude-command-from-history.md) ## Usage The `.vimrc` file for this project contains a function `CountTILs` that can be invoked with `c`. This will do a substitution count of the current number of TILs and display the result in the command tray. ## About I've written more about how this repo came to be in [How I Built a Learning Machine](https://dev.to/jbranchaud/how-i-built-a-learning-machine-45k9) and [A Decade of TILs](https://www.visualmode.dev/a-decade-of-tils). I shamelessly stole this idea from [thoughtbot/til](https://github.com/thoughtbot/til). ## Other TIL Collections * [Today I Learned by Hashrocket](https://til.hashrocket.com) * [jwworth/til](https://github.com/jwworth/til) * [til.simonwillison.net](https://til.simonwillison.net/) ## License © 2015-2026 Josh Branchaud This repository is licensed under the MIT license. See `LICENSE` for details. ================================================ FILE: Taskfile.yml ================================================ version: '3' vars: NOTES_DIR: notes NOTES_FILE: '{{.NOTES_DIR}}/NOTES.md' EDITOR: '{{.EDITOR | default "nvim"}}' tasks: default: desc: Show available commands cmds: - task --list notes: desc: Interactive picker for notes tasks cmds: - | TASK=$(task --list | grep "^\* notes:" | sed 's/^\* notes://' | sed 's/\s\+/ - /' | fzf --prompt="Select notes task: " --height=40% --reverse) || true if [ -n "$TASK" ]; then TASK_NAME=$(echo "$TASK" | awk '{print $1}' | sed 's/:$//') task notes:$TASK_NAME fi interactive: true silent: true notes:edit: desc: All-in-one edit, commit, and push notes cmds: - task notes:open - task notes:push notes:sync: desc: Sync latest changes from the notes submodule cmds: - cd {{.NOTES_DIR}} && git checkout main && git pull silent: false notes:open: desc: Opens NOTES.md (syncs latest changes first) in default editor deps: [notes:sync] cmds: - $EDITOR {{.NOTES_FILE}} interactive: true notes:push: desc: Commit and push changes to notes submodule dir: '{{.NOTES_DIR}}' cmds: - git add NOTES.md - git commit -m "Update notes - $(date '+%Y-%m-%d %H:%M')" - git pull --rebase - git push status: - git diff --exit-code NOTES.md silent: false notes:status: desc: Check status of notes submodule dir: '{{.NOTES_DIR}}' cmds: - git status notes:diff: desc: Show uncommitted changes in notes dir: '{{.NOTES_DIR}}' cmds: - git diff NOTES.md notes:log: desc: Show recent commit history for notes dir: '{{.NOTES_DIR}}' cmds: - git log --oneline -10 ================================================ FILE: ack/ack-bar.md ================================================ # ack --bar The [`ack`](https://beyondgrep.com/) utility has a fun Easter egg that dumps a Star Wars meme to the command line. Give it a try. ```bash $ ack --bar ``` See `man ack` for more details. ================================================ FILE: ack/case-insensitive-search.md ================================================ # Case-Insensitive Search Use the `-i` flag to perform a case-insensitive search with `ack`. ```bash $ ack -i easter ack/ack-bar.md 3:The [`ack`](https://beyondgrep.com/) utility has a fun Easter egg that dumps postgres/configure-the-timezone.md 18:Eastern time. ``` If you are a Vim user, you may be familiar with `smart-case`. The `--smart-case` option is a related Ack feature worth checking out. See `man ack` for more details. ================================================ FILE: ack/list-available-file-types.md ================================================ # List Available File Types The `ack` utility allows you to filter the searched files based on file type. If you'd like to know all of the file types available, you can use the `--help-types` flag. This will include file types you've specified in your `.ackrc` file. Here is a sample of some of the output. ``` $ ack --help-types ... --[no]css .css .less .scss --[no]dart .dart --[no]delphi .pas .int .dfm .nfm .dof .dpk .dproj .groupproj .bdsgroup .bdsproj --[no]elisp .el --[no]elixir .ex .exs --[no]erlang .erl .hrl --[no]fortran .f .f77 .f90 .f95 .f03 .for .ftn .fpp --[no]go .go --[no]groovy .groovy .gtmpl .gpp .grunit .gradle --[no]haskell .hs .lhs --[no]hh .h --[no]html .html .mustache .handlebars .tmpl --[no]jade .jade --[no]java .java .properties --[no]js .js ... ``` See `man ack` for more details. ================================================ FILE: ansible/loop-over-a-list-of-dictionaries.md ================================================ # Loop Over A List Of Dictionaries Ansible's `loop` can iterate over a list of dictionaries in a task. That task will be evaluated for each `item` in that list. Since each `item` is a dictonary, we can access the fields on the `item` directory with dot notation — `item.name`. Here is what this would look like for a task that is setting up authorized SSH keys. ```yaml --- - hosts: all vars: dev_users: - name: alice ssh_key_url: https://github.com/dev1.keys - name: bob ssh_key_url: https://github.com/dev2.keys tasks: - name: Set authorized keys taken from url ansible.posix.authorized_key: user: "{{ item.name }}" state: present key: "{{ item.ssh_key_url }}" loop: "{{ dev_users }}" ``` Notice the `loop` over the `dev_users` variable gives us access to an `item` in the task. Because each `item` has a `name` and an `ssh_key_url`, we can access those fields in the task. [source](https://docs.ansible.com/ansible/latest/playbook_guide/playbooks_loops.html#standard-loops) ================================================ FILE: astro/generate-types-for-a-content-collection.md ================================================ # Generate Types For A Content Collection Let's say I'm using Astro to publish posts via markdown. One of the best ways to do that is as a _Content Collection_. The posts will live in `src/content` probably under a `posts` directory. Plus a config file will define the collection and specify validations for the frontmatter. ```typescript // src/content/config.ts import { defineCollection, z } from 'astro:content'; const postsCollection = defineCollection({ schema: z.object({ title: z.string(), description: z.string(), tags: z.array(z.string()) }) }); export const collections = { 'posts': postsCollection, }; ``` When I first add this to my project and get the collection, it won't know what the types are. ```astro --- import { getCollection } from "astro:content"; export async function getStaticPaths() { const blogEntries = await getCollection("posts"); // ^^^ any return blogEntries.map((entry) => ({ params: { slug: entry.slug }, props: { entry }, })); } --- ``` I can tell Astro to generate a fresh set of types for things like content collections by running the [`astro sync` command](https://docs.astro.build/en/reference/cli-reference/#astro-sync). ```bash $ npm run astro sync ``` This updates auto-generated files under the `.astro` directory which get pulled in to your project's `env.d.ts` file. All of these types will also be synced anytime I run `astro dev`, `astro build`, or `astro check`. ================================================ FILE: astro/markdown-files-are-of-type-markdown-instance.md ================================================ # Markdown Files Are Of Type MarkdownInstance One of the things Astro excels at is rendering markdown files as HTML pages in your site. And at some point we'll want to access a listing of those markdown files in order to do something like display a list of them on an index page. For that, we'll use [`Astro.glob()`](https://docs.astro.build/en/reference/api-reference/#astroglob). ```typescript --- const allPosts = await Astro.glob("../posts/*.md"); ---
    {allPosts.map(post => { return })}
``` This looks great, but we'll run into a type error on that first line: `'allPosts' implicitly has type 'any'`. We need to declare the type of these post instances that are being read-in by Astro. These are of [type `MarkdownInstance`](https://docs.astro.build/en/reference/api-reference/#markdown-files). That's a generic though, so we need to tell it a bit more about the shape of a post. ```typescript import type { MarkdownInstance } from "astro"; export type BarePost = { layout: string; title: string; slug: string; tags: string[]; }; export type Post = MarkdownInstance; ``` We can then update that first line: ```typescript const allPosts: Post[] = await Astro.glob("../posts/*.md"); ``` Alternatively, you can specify the generic on `glob`: ```typescript const allPosts = await Astro.glob("../posts/*.md"); ``` ================================================ FILE: aws/aws-cli-requires-groff-executable.md ================================================ # AWS CLI Requires Groff Executable I have the AWS CLI installed on this machine, but when I went to run certain commands like `aws logs tail my_log_group` or even `aws logs tail help`, I'd get the following error: ``` $ aws logs tail help Could not find executable named 'groff' ``` This may only be an issue on MacOS Ventura for older versions of the CLI, per [this PR](https://github.com/aws/aws-cli/pull/7413): > The CLI's help commands are currently broken on macOS Ventura because Ventura has replaced groff with mandoc. This PR fixes the issue by falling back on mandoc if groff doesn't exist in the path. There are two ways of dealing with this. One would be to install the missing dependency, [`groff`](https://www.gnu.org/software/groff/): ```bash $ brew install groff ``` The other is to update the AWS CLI to one that falls back to `mandoc`. Depending on how you originally installed the AWS CLI, you can either [follow their official install/upgrade instructions](https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html), `pip install --upgrade awscli`, or upgrade view homebrew (`brew upgrade awscli`). ================================================ FILE: aws/find-and-follow-server-logs.md ================================================ # Find And Follow Server Logs Let's say you are authenticated with the AWS CLI and have the appropriate CloudWatch permissions. You have a few services running in production with associated logs. One of those is a Rails server. We want to run `aws logs tail`, but first we check how that command works. ```bash $ aws logs tail help ``` We see a bunch of options, but the only required one is `group_name` ("The name of the CloudWatch Logs group."). We may also notice the `--follow` flag which we'll want to use as well to keep incoming logs flowing. We need to determine the log group name for the Rails server. We can do that from the CLI as well (no need to dig into the web UI). ```bash $ aws logs describe-log-groups { "logGroups": [ { "logGroupName": "/aws/codebuild/fc-rails-app-abcefg-123456", "creationTime": 1739476650823, "metricFilterCount": 0, "arn": "arn:aws:logs:us-east-2:123456789:log-group:/aws/codebuild/fc-rails-app-abcefg-123456:*", "storedBytes": 65617, "logGroupClass": "STANDARD", "logGroupArn": "arn:aws:logs:us-east-2:123456789:log-group:/aws/codebuild/fc-rails-app-abcefg-123456" }, ... ] } ``` Because the group name is descriptive enough, we can find the log group we are interested in: `/aws/codebuild/fc-rails-app-abcefg-123456`. Now we know what we want to `tail`. ```bash $ aws logs tail /aws/codebuild/fc-rails-app-abcefg-123456 --follow ``` ================================================ FILE: aws/list-rds-snapshots-with-matching-identifier-prefix.md ================================================ # List RDS Snapshots With Matching Identifier Prefix I'm working on a script that manually creates a snapshot which it will then restore to a temporary database that I can scrub and dump. The snapshots that this script takes are _manual_ and they are named with identifiers that have a defining prefix (`dev-snapshot-`). Besides the few snapshots created by this script, there are tons of automated snapshots that RDS creates for backup/recovery purposes. I want to list any snapshots that have been created by the script. I can do this with the `describe-db-snapshots` command and some filters. ```bash $ aws rds describe-db-snapshots \ --snapshot-type manual \ --query "DBSnapshots[?starts_with(DBSnapshotIdentifier, 'dev-snapshot-')].DBSnapshotIdentifier" \ --no-cli-pager [ "dev-snapshot-20250327-155355" ] ``` There are two key pieces. The `--snapshot-type manual` filter excludes all those automated snapshots. The `--query` both filters to any snapshots whose identifier `?starts_with` the prefix `dev-snapshot-` and then refines the output to just the `DBSnapshotIdentifier` instead of the entire JSON object. [source](https://docs.aws.amazon.com/cli/latest/reference/rds/describe-db-snapshots.html) ================================================ FILE: aws/output-cli-results-in-different-formats.md ================================================ # Output CLI Results In Different Formats The AWS CLI can output the results of commands in three different formats. - Text - JSON - Table The _default_ output format for my AWS CLI is currently configured to `json`. ```bash $ aws configure get output json ``` I can either accept the default or I can override it with the `--output` flag. ```bash $ aws rds describe-db-instances \ --query 'DBInstances[*].Endpoint' \ --no-cli-pager [ { "Address": "fc-database-abcefg-ab1c23de.asdfgh4zxcvb.us-east-2.rds.amazonaws.com", "Port": 5432, "HostedZoneId": "A1BCDE2FG345H6" } ] $ aws rds describe-db-instances \ --query 'DBInstances[*].Endpoint' \ --no-cli-pager \ --output table ---------------------------------------------------------------------------------------------------- | DescribeDBInstances | +-----------------------------------------------------------------------+-----------------+--------+ | Address | HostedZoneId | Port | +-----------------------------------------------------------------------+-----------------+--------+ | fc-database-abcefg-ab1c23de.asdfgh4zxcvb.us-east-2.rds.amazonaws.com | A1BCDE2FG345H6 | 5432 | +-----------------------------------------------------------------------+-----------------+--------+ $ aws rds describe-db-instances \ --query 'DBInstances[*].Endpoint' \ --no-cli-pager \ --output text fc-database-abcefg-ab1c23de.asdfgh4zxcvb.us-east-2.rds.amazonaws.com A1BCDE2FG345H6 5432 ``` [source](https://docs.aws.amazon.com/cli/v1/userguide/cli-usage-output-format.html) ================================================ FILE: aws/sign-up-user-with-email-and-password.md ================================================ # Sign Up User With Email And Password [AWS Amplify](https://aws.amazon.com/amplify/) [Auth](https://docs.amplify.aws/lib/auth/getting-started/q/platform/js) offers both federated and username/password based authentication. Though the docs aren't clear, the required `username` parameter can be used as the email field with the [`signUp` API](https://aws-amplify.github.io/amplify-js/api/classes/authclass.html#signup). ```javascript import { Auth } from 'aws-amplify'; async function signUp({ email, password }) { try { const user = await Auth.signUp({ username: email, password, attributes: {}, }); console.log({ user }); } catch (error) { console.log('error signing up:', error); } } ``` Once the user has entered an email and password into the Sign Up form, those values can be passed to this `signUp` function. The `email` value is passed as the `username` and the `password` goes in as is. Amplify Auth will interpret the `username` as an email and register it as the contact email for this user. ================================================ FILE: aws/ssh-into-an-ecs-container.md ================================================ # SSH Into An ECS Container In [Connect To Production Rails Console on AWS / Flightcontrol](https://www.visualmode.dev/connect-to-production-rails-console-aws-flightcontrol), I went into full detail about how to access `rails console` for a production Rails app running in an ECS container. A big part of that process was establishing an SSH connection to the ECS container. To do that, I need to know my region, container ID, and task ID. I can get the first two by listing my clusters and finding the cluster/container that houses the Rails app. ```bash $ aws ecs list-clusters { "clusterArns": [ "arn:aws:ecs:us-east-2:123:cluster/rails-app-abc123" ] } ``` The region then is `us-east-2` and the container ID is `rails-app-abc123`. I can use that to find the task ID: ```bash $ aws ecs list-tasks --region us-east-2 --cluster rails-app-abc123 { "taskArns": [ "arn:aws:ecs:us-east-2:123:task/rails-app-abc123/8526b3191d103bb1ff90c65a655ad004" ] } ``` The task ID is the final portion of the URL: `8526b3191d103bb1ff90c65a655ad004`. Putting this all together I can SSH into the ECS container with a bash profile like so: ```bash $ aws ecs execute-command \ --region us-east-2 \ --cluster rails-app-abc123 \ --container rails-app-abc123 \ --task 8526b3191d103bb1ff90c65a655ad004 \ --interactive \ --command "/bin/bash" ``` ================================================ FILE: aws/turn-off-output-pager-for-a-command.md ================================================ # Turn Off Output Pager For A Command It is not uncommon for an AWS CLI command to return a ton of output. When that happens, it is nice that the results end up in pager program (like `less`) where you can search and review them, copy a value of interest, and then exit. The pager prevents that wall of output from cluttering your terminal history. However, sometimes I am running a command that I know is going to return a small result. I'd rather have the results go to stdout where I can see them in the terminal history rather than to an ephemeral pager. For that situation I can tack on the `--no-cli-pager` flag. ```bash $ aws rds describe-db-instances \ --query 'DBInstances[*].EngineVersion' \ --output json \ --no-cli-pager [ "13.15", "16.8" ] ``` Here I've asked the AWS CLI to tell me the engine versions of all my RDS Postgres databases. Because I know the results are only going to include a couple results for my couple of DBs, I'd like to skip the pager — `--no-cli-pager`. Though I think it is better to do this on a case by case basis, it is also possible to turn off the pager via the CLI configuration file. ```bash $ aws configure set cli_pager "" ``` [source](https://docs.aws.amazon.com/cli/latest/userguide/cli-usage-pagination.html#cli-usage-pagination-clientside) ================================================ FILE: aws/use-specific-aws-profile-with-cli.md ================================================ # Use Specific AWS Profile With CLI I have multiple AWS profiles authenticated with the AWS CLI. For some projects I need to use the `default` one and for others I need to use the other. First, I can list the available profiles like so: ```bash $ aws configure list-profiles default dev-my-app ``` For one-off commands I can specify the profile for any AWS CLI command using the `--profile` flag. ```bash $ aws ecs list-clusters --profile josh-visualmode ``` However, I don't want to have to specify that flag every time when I'm working on a specific project. Instead I can specify the profile with an environment variable. The [`direnv`](https://direnv.net/) tool is a great way to do this on a per-project / per-directory basis. I can create or update the `.envrc` file (assuming I have `direnv` installed) adding the following line (and re-allowing the changed file): ``` # .envrc export AWS_PROFILE=dev-my-app ``` Now, any AWS command I issue from that directory or its subdirectories will use that profile by default. [source](https://docs.aws.amazon.com/cli/v1/userguide/cli-configure-files.html#cli-configure-files-using-profiles) ================================================ FILE: bash/edit-the-current-command-prompt.md ================================================ # Edit The Current Command Prompt A neat feature of `bash` is the ability to open whatever the current state of the command prompt is into your default editor. Let's say we have a really long command that we've just tried to run, but it failed and we need to make a small change somewhere in the middle. Instead of holding the left arrow key for 30 seconds, we can instead hit `CTRL-X CTRL-E`. This pops us into our `EDITOR` (or maybe `VISUAL`, not sure which). In my case, that is `nvim`. I now have access to all the features I'm used to in `nvim` for quickly navigating to and editing, searching and replacing, or whatever. Once I've got the command how I like it, I can save and exit (`:wq`) and the updated command will be executed. This is similar to [the `fc` builtin](unix/fix-previous-command-with-fc.md), which also happens to be available for `zsh`. ================================================ FILE: brew/clean-up-your-brew-installations.md ================================================ # Clean Up Your Brew Installations Over time as you upgrade brew-installed programs and make changes to your `Brewfile`, your machine will have artifacts left behind that you no longer need. Periodically, it is good to clean things up. First, you can get a summary of stale and outdated files that brew has installed. Use the `--dry-run` flag. ```bash $ brew cleanup --dry-run ``` If you feel good about what you see in the output, then give things a clean. ```bash $ brew cleanup ``` Second, if you are using a `Brewfile` to manage what `brew` installs, then you can instruct `brew` to uninstall any dependencies that aren't specified in that file. By default it operates as a dry run and the `--force` flag will be needed to actually do the cleanup. And specify the filename if it doesn't match the default of `Brewfile`. ```bash $ brew bundle cleanup --file=Brewfile.personal ``` If the output looks good, then force the cleanup: ```bash $ brew bundle cleanup --force --file=Brewfile.personal ``` See `brew cleanup --help` and `brew bundle --help` for more details. ================================================ FILE: brew/configure-brew-environment-variables.md ================================================ # Configure Brew Environment Variables The `brew` CLI can be configured with a ton of different environment variables. A full listing of those can be found in the [Environment section of their docs](https://docs.brew.sh/Manpage#environment). If you want to change the defaults of any of those values, you can either set them directly in your environment: ```bash $ set HOMEBREW_BAT=1 ``` Or you can set them in a more dedicated place like one of Homebrew's environment files. There are a couple possible locations for these files. I prefer to use `$HOME/.homebrew/brew.env` (i.e. `~/.homebrew/brew.env`). ``` HOMEBREW_BAT=1 ``` This file and directly likely don't exist, so you may have to set them up the first time: ``` $ mkdir $HOME/.homebrew $ touch $HOME/.homebrew/brew.env ``` ================================================ FILE: brew/export-list-of-everything-installed-by-brew.md ================================================ # Export List Of Everything Installed By Brew If you're on a Mac using Homebrew to install various tools and utilities, there may come a time when you want a listing of what is installed. Run this command: ```bash $ brew bundle dump ``` It may take 10 or so seconds. When it is done, you'll have a `Brewfile` in your current directory. Open it up and you'll see a bunch of lines like the following: ``` tap "heroku/brew" tap "homebrew/bundle" tap "homebrew/services" tap "mongodb/brew" tap "planetscale/tap" tap "stripe/stripe-cli" brew "asdf" brew "bat" brew "direnv" brew "entr" brew "exa" brew "fd" brew "ffmpeg" brew "fx" brew "fzf" brew "gcc" brew "gh" brew "planetscale/tap/pscale" brew "stripe/stripe-cli/stripe" cask "1password-cli" vscode "ms-playwright.playwright" vscode "ms-vsliveshare.vsliveshare" vscode "prisma.prisma" ``` Notice there are `tap`, `brew`, `cask`, and even `vscode` directives. This is a file you could export and then run on a 'new' machine to install all the programs you're used to having available on your current machine. [source](https://danmunoz.com/setting-up-a-new-computer-with-homebrew/) ================================================ FILE: brew/install-from-nonstandard-brewfile.md ================================================ # Install From Nonstandard Brewfile When you want to install the packages listed in the `Brewfile` for your current project (or dotfiles), you can run: ```bash $ brew bundle ``` And `brew` knows to look for and use the `Brewfile` in the current directory. If, however, you are trying to run `brew bundle` for a `Brewfile` located somewhere besides the current directory *OR* you want to target a file with a non-standard name (like [`Brewfile.personal`](https://github.com/jbranchaud/dotfiles/blob/main/Brewfile.personal)), then you can use the `--file` flag. ```bash $ brew bundle --file Brewfile.personal ``` This is what I do [here in my `dotfiles` repo](https://github.com/jbranchaud/dotfiles/blob/b053f6251cae7ed52f698fc2a2c40ba82c5881b0/installer/mac-setup.sh#L42-L48). See `man brew` and find the section on `brew bundle` for more details. ================================================ FILE: brew/install-go-packages-in-brewfile.md ================================================ # Install Go Packages In Brewfile Typically my `Brewfile` is only full of `brew` and `cask` directives. That's starting to change now that `brew` supports installing Go packages listed in the `Brewfile`. Use the `go` directive and the URL to the hosted Go package. Here is an example of a `Brewfile` that includes a `cask`, `brew`, and `go` directive. ``` # screen resolution tool cask "betterdisplay" # Mac keychain management, gpg key brew "pinentry-mac" # Sanitized production Postgres dumps go "github.com/jackc/pg_partialcopy" ``` I've recently added the exact package from above to my [`dotfiles` repo](https://github.com/jbranchaud/dotfiles/commit/e83e9d19504f0e2f95eba33123f907f999bf865e). Here is the [PR to `brew`](https://github.com/Homebrew/brew/pull/20798) where this functionality was added back in October of 2025. ================================================ FILE: brew/list-all-services-managed-by-brew.md ================================================ # List All Services Managed By Brew Daemonized services, such as PostgreSQL, can be installed and managed with Homebrew. Under the hood `brew` uses `launchctl` on Mac to manage these services — i.e. starting, restarting, and stopping them. Assuming you've already installed some services, you can run `brew services list` to see what services there are and what their current status is. ```bash $ brew services list Name Status User File mailhog none mysql none postgresql@11 started jbranchaud ~/Library/LaunchAgents/homebrew.mxcl.postgresql@11.plist postgresql@13 none postgresql@16 none unbound none ``` This is the default behavior if you just run `brew services` without a subcommand. This is helpful if you are, for instance, trying to see which PostgreSQL server version you are currently running and which other ones are available to run. I might then issue a `stop` to `postgresql@11` so that I can then `start` the `postgresql@16` service. See `brew services --help` for more details. ================================================ FILE: chrome/access-a-value-logged-to-the-console.md ================================================ # Access A Value Logged To The Console Did your app just log an object to the dev tools console and you'd like to interact with that object? It's not straightforward, but you can do it. Assuming you already have dev tools opened to the _console_ tab, right click on the value that has been logged to the console. Select the _Store as Global Variable_ option. This will re-log the value assigning it to the `temp1` variable. You can now reference that object as `temp1` accessing its values and calling functions. You can even do this with multiple logged values, each subsequent one will be assigned incrementing variable names: `temp2`, `temp3`, etc. [source](https://stackoverflow.com/questions/15895579/access-last-logged-value-in-chrome-console) ================================================ FILE: chrome/chrome-supports-many-unix-keyboard-shortcuts.md ================================================ # Chrome Supports Many Unix Keyboard Shortcuts In a Unix environment, you can use keyboard shortcuts to do a variety of things more efficiently. For instance, hitting `ctrl-a` moves the cursor to the beginning of the line. Conversely, `ctrl-e` moves the cursor to the end of the line. Another fun one is `ctrl-u` which erases from the cursor to the beginning of the line. Learning these doesn't only have value when you are logged into some remote server. There are many apps that support a variety of these shortcuts. Chrome is one of them. ![examples of unix shortcuts in chrome text area](https://i.imgur.com/7MfNe6c.gif) ================================================ FILE: chrome/copy-some-data-from-the-console.md ================================================ # Copy Some Data From The Console Sometimes you have some data that you are playing around with in the console, something you logged from an API response. You then want to share it, so you try to copy the whole thing into your system copy buffer. There are a couple hacky ways of doing this, but Chrome supports a really smooth way. Use the `copy` function. ```javascript characters > (8) [{…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}] copy(characters[1]) ``` My system copy buffer now contains the entire object that makes up the second entry in that list. I can then paste it into Slack or wherever. [source](https://twitter.com/addyosmani/status/1092686766375616517) ================================================ FILE: chrome/duplicate-the-current-tab.md ================================================ # Duplicate The Current Tab Sometimes when viewing a page, you realize you want to keep that page open but also go back to the previous page to view something else. An easy way of achieving this is to duplicate the current tab and then go back. To duplicate the current tab hit `Cmd+Enter` while the focus is on the URL bar. If the URL bar is not in focus, then first hit `Cmd+L` to focus followed by `Cmd+Enter`. ================================================ FILE: chrome/easier-access-to-network-throttling-controls.md ================================================ # Easier Access To Network Throttling Controls In [Simulating Various Network Connection Speeds](https://github.com/jbranchaud/til/blob/master/chrome/simulating-various-connection-speeds.md), I showed how to change between various simulated connection speeds from the _Network_ tab in Chrome devtools. Unfortunately, the Connection Speed dropdown is crowded out by a number of other controls in the _Network_ tab. As a result, unless that tab is expanded pretty wide, you won't be able to get at it. I've found myself sliding the devtools wider and narrower over and over while testing things with throttling. There is another, easier place to access throttling. ![](https://i.imgur.com/fHN8F28.gif) The console drawer gives us access to a number of additional tabs of controls. Add the _Network connections_ tab for easier access. ================================================ FILE: chrome/keybinding-to-focus-the-address-bar.md ================================================ # Keybinding To Focus The Address Bar The address bar (URL bar) in Chrome is versatile. You can enter both URLs and search terms into it. If I want to enter a new search term, I generally have to reach for my mouse, double click in the address bar, and then type in that term. Chrome has a keybinding that sends focus to the address bar, which cuts some of those steps. Hit `Cmd-L`. You'll see that the address bar gets focus with the entire address highlighted. You can begin typing your new search term immediately. [source](https://etc.usf.edu/techease/4all/input-devices/google-chrome-keyboard-shortcuts/) ================================================ FILE: chrome/navigate-the-browser-history-with-vimium.md ================================================ # Navigate The Browser History With Vimium The [Vimium](https://vimium.github.io/) Chrome extension is great for moving around the current page you are on. It can also be used to navigate back and forth through your browser history. You can go back to the previous page with `H` (`Shift-h`). You can go forward to a page you went back from with `L` (`Shift-l`). ================================================ FILE: chrome/open-current-tab-in-new-window-with-vimium.md ================================================ # Open Current Tab In New Window With Vimium Sometime I have a busy Chrome window going with a bunch of tabs open for various lines of work as well as a number of tabs that I've neglected to close. I then open a new tab, find something useful, and realize I'm at a "branching point". I'm about to start in on a specific chunk of work that will probably involve opening several more tabs and switch back and forth between some dashboards. I want to start all of this from a fresh slate -- or at least from a fresh Chrome window. With [Vimium](https://github.com/philc/vimium), I can hit `W` (`Shift-w`) to have the current tab move from the current window to a new window. The original window, minus that one tab, will be left as is so that I can go back to it as needed. ================================================ FILE: chrome/pause-javascript-from-the-source-devtools-panel.md ================================================ # Pause JavaScript From The Source DevTools Panel You know that moment when you accidentally introduce an infinite loop in some JavaScript code. You've just refreshed the page in the browser and everything seems to both hang and loop. And the browser slows to a crawl. If you're trying to get to the bottom of things, you may be `console.log`'ing some data points. However, because of the infinite loop, the logs are flying by in the console. It would be great if you could bring things to a halt. I assume you already have devtools open, since you're looking at the logs. If not, hit `F12`. Then go to the _Sources_ tab which is where the JavaScript debug tools are located. From here, you can find and click the pause button. It should be the first in the panel of debug controls. Alternatively, as long as you have the _Source_ tab in focus, you can simply hit `F8`. Once you've paused that infinite loop, you can go back to the console and have a look around. ================================================ FILE: chrome/pretty-print-tabular-data.md ================================================ # Pretty Print Tabular Data Looking at a bunch of data in the Chrome dev tools console isn't great. It can be a bit difficult to read because of the way it is displayed. Fortunately, the Chrome dev tools come with a handy way of displaying tabular data, `console.table()`. If you give `console.table` an array of objects or array of arrays, it will format it in a table like so: ![](http://i.imgur.com/LPgBpRB.png) ```js console.table([{one: 1, two: 2, three: 3}]) ``` ================================================ FILE: chrome/reference-the-selected-node.md ================================================ # Reference The Selected Node In the Chrome dev tools, if you've selected (highlighted) a node in the DOM, you can reference that node from the console with `$0`. This is handy if you are debugging or exploring certain parts of a page and need to run commands against that node. For instance, if you were to select the `` node in the DOM, you could then programmatically check the `lang` attribute from the console like so: ``` > $0.lang // "en-US" ``` If there is `jQuery` on the page and you've selected the node that contains all of the page's content, you can do something like the following: ``` > $($0).html('

Hello, World!

') ``` ================================================ FILE: chrome/search-tabs-with-the-vimium-vomnibar.md ================================================ # Search Tabs With The Vimium Vomnibar If you use Chrome like I do, then you eventually end up with several windows with dozens if not 100+ tabs open. It can start to get tedius with that many tabs to find and navigate to a given tab. Someone might suggest closing a few dozen tabs as a solution to this predicament. However, Vimium offers a solution that doesn't require I [_kill my darlings_](https://en.wiktionary.org/wiki/kill_one%27s_darlings). The Vomnibar, a Vimium-powered search bar, can be summoned with `T` to only search through open tabs. When I hit `T`, I see a text area (for refining the search) and then a bunch of entries populate below that which I immediately recognize as many of those tabs that I'm going to get back to one of these days. To narrow down to the specific thing I'm looking for, I type something into the input. Then I arrow to the result I'm looking for and hit enter. And I'm transported to that tab. If I don't like where I ended up, I can also go back to the tab I had been on with `^`. ================================================ FILE: chrome/selecting-dom-elements-faster-than-ever.md ================================================ # Selecting DOM Elements Faster Than Ever Selecting and inspecting DOM elements: you've done it many times before. Whether you right click the element and select _Inspect_ (which isn't always all that accurate) or you use the DevTools' inspect tool with highlight-assist, it takes a couple clicks to get there. There is a faster way. Hit `Cmd-Shift-C`. Chrome DevTools will be expanded open if it isn't already and your mouse pointer will be put in inspect mode with the highlight-assist. Find your DOM element, give it a click, and start inspecting! ================================================ FILE: chrome/simulating-various-connection-speeds.md ================================================ # Simulating Various Connection Speeds I spend everyday building web apps from a machine that has a wired connection to the internet. Though I spend a lot of time loading various pages and experiencing the app like a user might, I end up having a pretty narrow perspective. What will this app be like for people on various qualities of mobile connections? Chrome has a feature built in to its devtools that makes it easy to throttle your connection to simulate various speeds. Open up devtools with `Cmd+Opt+J`, navigate to the _Network_ tab, and then open the throttling drop down. ![](http://i.imgur.com/EI3H9Oe.png) From here we can select the connection speed we want to simulate. If we then reload the page, we will not only experience the page load at that speed, we will also see the numbers in that _Network_ tab. ================================================ FILE: chrome/toggle-device-mode.md ================================================ # Toggle Device Mode Working on some styles? Want to make sure those minor tweaks look good on both desktop and mobile? Your probably moving back and forth between the desktop mode and one of the smaller device modes. There is a keybinding for this -- `Cmd-Shift-M` The chrome devtools panel will need to be open and in focus for this to work. ================================================ FILE: chrome/toggle-open-the-console-drawer.md ================================================ # Toggle Open The Console Drawer With Chrome devtools open and in focus, hit ``. The console drawer will be toggled open. Hit `` again and it will be toggle closed. ================================================ FILE: chrome/trigger-commands-from-the-devtools-command-palette.md ================================================ # Trigger Commands From The Devtools Command Palette There are a ton of tabs, drop downs, and nested menus in Chrome's devtools. If I know what I am looking for, that is great, I can navigate to it without much trouble. But for other features and commands, I'm stumped and can end up spending minutes looking around. For example, where is the option to 'Disable JavaScript'? I don't know. And I don't need to know. Instead of searching around for it, I can pop open the devtools _command palette_ with `cmd+shift+p`. This is a modal menu that prompts me to search for a command to run. I start typing `disab` and already `Disable JavaScript` appears as one of the top options. I can select that option and JavaScript will be disabled. When I'm ready to turn it back on. I can hit `cmd+shift+p` again and search for `enab`. The `Enable JavaScript` option appears and I can select it to turn it back on. Note: you'll need to have the devtools panel open and your focus will need to be on it for the keybinding to be picked up. [source](https://developer.chrome.com/docs/devtools/command-menu) ================================================ FILE: chrome/view-network-traffic-for-new-tabs.md ================================================ # View Network Traffic For New Tabs Generally when you open a new tab and pop open the Network panel in the chrome devtools, you won't immediately see any traffic. This is because the initial network requests to load the site happened before you accessed the Network panel. If you'd like to view the Network traffic immediately without having to refresh the new tab, you'll have to make the devtools panel open automatically with new tabs. 1. Click the _triple dot_ icon from the top of the devtools panel 2. Click 'Settings' 3. Find the 'DevTools' section 4. Check the box for 'Auto-open DevTools for popups' Now, whenever you open a new tab, Chrome devtools will be open and the initial network traffic will be recorded. [source](https://stackoverflow.com/questions/16210468/chrome-dev-tools-how-to-trace-network-for-a-link-that-opens-a-new-tab) ================================================ FILE: claude-code/allow-edits-from-the-start.md ================================================ # Allow Edits From The Start A common pattern for me when using Claude Code is that I start it up in a project, I prompt it with a question or feature spec, it either comes up with a plan or just starts working, and as soon as it is ready to make its first edits to a file, it prompts me something like: ``` Do you want to make this edit to Taskfile.yml? ❯ 1. Yes 2. Yes, allow all edits during this session (shift+tab) 3. Type here to tell Claude what to do differently ``` That's a nice default so that I don't get surprised by Claude Code editing a bunch of files. However, if I'm in a git-backed project and I'm going into a session intending to make edits, then I can skip the formalities. I can tell Claude Code when starting up the session that edits are allowed. ```sh $ claude --permission-mode acceptEdits ``` When I do this, I'll see the following indicator below the prompt input field: ``` ⏵⏵ accept edits on (shift+tab to cycle) ``` If I've already started `claude` but I forgot to specify that permission mode, I can also toggle right into _accept edits_ by hitting `Shift+Tab`. [source](https://www.youtube.com/watch?v=_IK18goX4X8) ================================================ FILE: claude-code/monitor-usage-limits-from-cli.md ================================================ # Monitor Usage Limits From CLI When I first started using Claude Code enough to push the usage limits, I would periodically switch over to the browser to check `https://claude.ai/settings/usage` to see how close I was getting. That page would tell me what percentage of my allotted usage I had consumed so far for the current 5-hour session and then how long until that 5-hour usage window resets. This can also be viewed directly in Claude Code for the CLI. First, run the `/status` slash command and then _tab_ over to the _Usage_ section. There you will see the same details as in the web view. I'm also learned, as I write this, that you can go directly to the _Usage_ section by typing the `/usage` slash command. See [the docs](https://code.claude.com/docs/en/slash-commands) for a listing of all slash commands. ================================================ FILE: claude-code/open-current-prompt-in-default-editor.md ================================================ # Open Current Prompt In Default Editor [Claude Code](https://www.claude.com/product/claude-code) gives you a single line to write a prompt. You can write and write as much as you want, but it will all be on that single line. And avoid accidentally hitting 'Enter' before you're done. I found myself wanting to space out my thoughts, create a code block as part of a prompt, and generally have a scratch pad instead of just a text box. By hitting `ctrl-g`, I can move the current prompt into my default editor (in my case, `nvim`). From there I can continue to write, edit, and format with all the affordances of an editor. Once I'm done crafting the prompt, I can save (e.g. `:wq`) and Claude Code will be primed with that text. I can then hit 'Enter' to let `claude` do its thing. ================================================ FILE: claude-code/resume-specific-session.md ================================================ # Resume Specific Session There are a few different ways to resume a [Claude Code](https://code.claude.com/docs/en/overview) session. First, if I have exited a session for the current project and I want to pick back up with that most recent one, then I can use `claude --continue`. If I have had a few recent sessions for the current project and I want to remember what they were and pick up where I left off with one of them, then I can use `claude --resume` (with no argument). That will open a picker where I can browser through a summary of the recent sessions based on their starting prompt. The one I pick is the session that will be resumed. Finally, if I have grabbed a specific session ID (UUID) during the session from the `/status` output, then I can reference that value directly. ```sh $ claude --resume 92170532-be31-4a91-b2a9-025b8fa78232 ``` See `claude --help` for more details. ================================================ FILE: clojure/aggregation-using-merge-with.md ================================================ # Aggregation Using merge-with Clojure provides the `merge-with` function as a way of conjoining a series of maps. You must provide `merge-with` a function that it can use to merge two values for matching keys. For instance, imagine you have a bunch of maps that contain counts for entities identified by keywords. You can consolidate the sum of all the counts into a single map using the `merge-with` function combined with the `+` function. ```clojure > (merge-with + {:a 1 :b 3} {:b 2 :c 3} {:c 1 :d 4}) {:a 1, :b 5, :c 4, :d 4} ``` For different kinds of data, a different function argument may be more appropriate. For instance, aggregating lists instead of integers calls for the `concat` function: ```clojure > (merge-with concat {:a '(1 2) :b '(3 4)} {:c '(3 4) :a '(5 4 1)}) {:a (1 2 5 4 1), :b (3 4), :c (3 4)} ``` ================================================ FILE: clojure/argument-requirements-for-a-function.md ================================================ # Argument Requirements For A Function When defining a function, you must declare one or more function definitions, each of which will require a different set of arguments. These argument lists are stored as metadata for the function. So, if you are trying to figure out what arity a function is or what variations of arguments it takes, you can check the metadata like so: ```clojure > (:arglists (meta #'str)) ([] [x] [x & ys]) ``` [source](http://stackoverflow.com/questions/1696693/clojure-how-to-find-out-the-arity-of-function-at-runtime) ================================================ FILE: clojure/combinations-of-items-from-a-sequence.md ================================================ # Combinations Of Items From A Sequence Sometimes we want all combinations of items from a list. For instance, we may have 5 people and we want to know all the ways that we can unique pair up 2 people from that group of 5. What we want is the number of combinations of 2 people from the 5. The [clojure/math.combinatorics](https://github.com/clojure/math.combinatorics) library provides a `combinations` function that gives us exactly that functionality. ```clojure (use '[clojure.math.combinatorics :as combo]) (combo/combinations ["Liz", "Tracy", "Kenneth", "Jack", "Jenna"] 2) ; (("Liz" "Tracy") ("Liz" "Kenneth") ("Liz" "Jack") ; ("Liz" "Jenna") ("Tracy" "Kenneth") ("Tracy" "Jack") ; ("Tracy" "Jenna") ("Kenneth" "Jack") ("Kenneth" "Jenna") ; ("Jack" "Jenna")) ``` The `combinations` function takes a list of items and then a number for the size of the grouping combinations that it is supposed to produce. ================================================ FILE: clojure/define-something-only-once.md ================================================ # Define Something Only Once Clojure provides [`defonce`](https://clojuredocs.org/clojure.core/defonce) which is a macro that defines something only once. Once a variable has been bound to a value, subsequent attempts to do `defounce` for that variable will go unevaluated. This will have no implications for how the `def` special form works. Here is an example: ```clojure (defonce stuff 5) #'user/stuff user=> (defonce stuff "what") nil user=> stuff 5 user=> (def stuff "okay") #'user/stuff user=> stuff "okay" ``` ================================================ FILE: clojure/evaluate-one-liners-with-lein-exec.md ================================================ # Evaluate One Liners With lein-exec You can install the [`lein-exec`](https://github.com/kumarshantanu/lein-exec) plugin and then use it to evaluate one liner bits of code with the `-e` flag. ```bash $ lein exec -e '(println "foo" (+ 20 30))' foo 50 ``` ================================================ FILE: clojure/expanding-macros.md ================================================ # Expanding Macros Macros are an important part of Clojure's syntax. They allow you to write cleaner, terser, more expressive code. Though sometimes you may want to inspect the clojure code that is actually produced by a particular macro. The `macroexpand` function allows for just this. For instance, if you have a snippet of code using the `->>` operator: ```clojure (->> 4 (+ 1) (- 2) (* 3)) ``` You can wrap that form with the `macroexpand` function to see the form that is ultimately evaluated: ```clojure > (macroexpand (->> 4 (+ 1) (- 2) (* 3))) ; (* 3 (- 2 (+ 1 4))) ``` It doesn't buy us much in a contrived example like this, but can prove useful for better understanding clojure and the more complex code we write. ================================================ FILE: clojure/get-the-value-of-an-environment-variable.md ================================================ # Get The Value Of An Environment Variable You can get the value of an environment variable on your system using the `System/getenv` function. Just pass it the environment variable as a string: ```clojure > (System/getenv "HOME") "/Users/jbranchaud" ``` It returns `nil` when the environment variable doesn't exist. ```clojure > (System/getenv "HOUSE") nil ``` ================================================ FILE: clojure/list-functions-for-a-namespace.md ================================================ # List Functions For A Namespace You know that `clojure.string` has a function for uppercasing a string, but you can't quite remember the name of the function. You'd remember if you saw the name though. What you'd like to do is list all the functions in the `clojure.string` namespace to see if you can pick it out. You can do just that. There are a couple ways to do it, in fact. You can use the `dir` function with Clojure 1.6+. Alternatively, you can grab all the keys from the public intern mappings of the namespace. ```clojure > (dir clojure.string) blank? capitalize ends-with? escape includes? index-of join last-index-of lower-case re-quote-replacement replace replace-first reverse split split-lines starts-with? trim trim-newline triml trimr upper-case nil > (keys (ns-publics 'clojure.string)) (ends-with? capitalize reverse join replace-first starts-with? escape last-index-of re-quote-replacement includes? replace split-lines lower-case trim-newline upper-case split trimr index-of trim triml blank?) ``` [source](http://stackoverflow.com/questions/2747294/how-to-list-the-functions-of-a-namespace) ================================================ FILE: clojure/load-a-file-into-the-repl.md ================================================ # Load A File Into The REPL You can quickly load a file into a REPL session using the `load-file` function. You can specify an absolute or relative path and it will > sequentially read and evaluate the set of forms contained in the file. ```clojure (load-file "path/to/file.clj") ``` See the [`load-file` docs](https://clojuredocs.org/clojure.core/load-file) for more details. ================================================ FILE: clojure/mapping-with-an-index.md ================================================ # Mapping With An Index If you're mapping over a collection and you need an index for each item, you can reach for `map-indexed`. The `map-indexed` function can be used like so: ```clojure > (def foods ["pizza" "hotdog" "chicken alfredo"]) #'cljs.user/foods > (map-indexed (fn [idx item] (str idx " - " item)) foods) ("0 - pizza" "1 - hotdog" "2 - chicken alfredo") ``` Alternatively, `map` can take multiple sequences, and each becomes a new argument to the mapping function. By giving an infinite sequential list of numbers starting at 0 as the first sequence, you can pretend they're indices, like so: ```clojure > (map (fn [idx item] (str idx " - " item)) (range) foods) ("0 - pizza" "1 - hotdog" "2 - chicken alfredo") ``` ================================================ FILE: clojure/open-javadocs.md ================================================ # Open JavaDocs Clojure has all kinds of Java interop including a fancy function called `javadoc`. The `javadoc` function _opens a browser window displaying the javadoc for the argument_. Thinking about using Java's `ArrayList` class, but want to read up on it first? Try ``` > (javadoc java.util.ArrayList) true ``` The javadoc page for [ArrayList](http://docs.oracle.com/javase/6/docs/api/java/util/ArrayList.html) will be opened up in your default browser. ================================================ FILE: clojure/pretty-print-the-last-thing.md ================================================ # Pretty Print The Last Thing Clojure provides `pp` as a convenience macro for pretty printing the last thing that was output. If you are playing around with a function in the repl, trying to get the output just right, `pp` can come in handy. ```clojure > (fancy-func) {:one {:a 1, :b 2, :c 3, :d 4}, :two {:b 2, :c 3, :d 4, :e 5}, :three {:c 3, :d 4, :e 5, :f 6}, :four {:d 4, :e 5, :f 6, :g 7}} > (clojure.pprint/pp) {:one {:a 1, :b 2, :c 3, :d 4}, :two {:b 2, :c 3, :d 4, :e 5}, :three {:c 3, :d 4, :e 5, :f 6}, :four {:d 4, :e 5, :f 6, :g 7}} nil ``` See `(doc pp)` for more details. ================================================ FILE: clojure/quick-clojure-docs.md ================================================ # Quick Clojure Docs Assuming you already have the [`fireplace.vim`](https://github.com/tpope/vim-fireplace) plugin installed and a repl running, you can quickly pull up the docs for any clojure function. Navigate the cursor over the keyword in question and hit `K` in normal mode. This will instruct `fireplace.vim` to use the `doc` function on the symbol for that keyword. The docs associated with that keyword will be displayed at the bottom of the window. ================================================ FILE: clojure/reductions.md ================================================ # Reductions One of the core functions in Clojure is [`reduce`](https://clojuredocs.org/clojure.core/reduce). It allows you to build up some result based on applying a function to each value in a collection. Clojure provides a similar function that builds up a (lazy) sequence of intermediate values as it is performing a reduce. This function is [`reductions`](https://clojuredocs.org/clojure.core/reductions). Using `reduce` to sum a collection of integers looks like this ```clojure > (reduce + 0 [1 2 3 4 5]) => 15 ``` whereas `reductions` performing the same task will look like this ```clojure > (reductions + 0 [1 2 3 4 5]) => (0 1 3 6 10 15) ``` h/t Josh Davey ================================================ FILE: clojure/set-max-heap-size.md ================================================ # Set Max Heap Size For `lein`-based projects, the maximum heap size can be set by including the `-Xmx` option in the `jvm-opts` portion of the `project.clj` file. For instance, to set the maximum JVM heap size to 2 gigabytes, include: ```clojure :jvm-opts ["-Xmx2g"] ``` in your `project.clj` file. ================================================ FILE: clojure/specify-the-directory-of-a-shell-command.md ================================================ # Specify the Directory of a Shell Command Clojure gives us access to Java's shell capabilities through `clojure.java.shell`. For instance, if you want to list the contents of your project's directory, you can issue an `ls` command: ``` > (clojure.java.shell/sh "ls") ; {:exit 0, ; :out "LICENSE\nREADME.md\ndoc\nproject.clj\nresources\nsrc\ntarget\ntest\n", ; :err ""} ``` The default will always be to execute the command in the directory of the containing project. It is likely that you'd like to specify a different directory though. There is a function for that: ```clojure (clojure.java.shell/with-sh-dir "some/dir" (clojure.java.shell/sh "ls")) ``` Or more concisely, you can specify the directory as part of the `sh` function: ```clojure (clojure.java.shell/sh "ls" :dir "some/dir") ``` ================================================ FILE: clojure/splitting-on-whitespace.md ================================================ # Splitting On Whitespace If you have a string with spaces and you want to split the string into a vector of strings (delimited by the spaces), then you can do something like this: ```clojure (clojure.string/split "split me up" #" ") ; ["split" "me" "up"] ``` However, if you have extra spaces in your string, the output may not be quite what you want: ```clojure (clojure.string/split "double spacing wtf?" #" ") ; ["double" "" "spacing" "" "wtf?"] ``` A quick fix might look like this: ```clojure (clojure.string/split "double spacing wtf?" #" +") ; ["double" "spacing" "wtf?"] ``` That's nice, but it is going to fall over as soon as we run into input with tabs and new lines. Assuming we want to split on all whitespace, we should tell our regular expression to do just that: ```clojure (clojure.string/split "we\thave new\nlines and tabs\n" #"\s+") ; ["we" "have" "new" "lines" "and" "tabs"] ``` ================================================ FILE: clojure/swap-two-items-in-a-vector.md ================================================ # Swap Two Items in a Vector If you want to replace the value at an index in a vector, you can use the `assoc` function supplied by `clojure.core` like so: ```clojure > (assoc [5 6 7 8] 1 9) ; [5 9 7 8] ``` Likewise, if you want to replace two items in a vector, you can extend the above like so: ```clojure > (assoc [5 6 7 8] 1 2 2 4) ; [5 2 4 8] ``` We can take advantage of that second example to construct a function that will swap two values: ```clojure (defn swap [items i j] (assoc items i (items j) j (items i))) ``` This function will break on values of `i` and `j` that are out of the bounds of `items`, but dealing with that is left as an exercise to the reader. [source](http://stackoverflow.com/questions/5979538/what-is-the-idiomatic-way-to-swap-two-elements-in-a-vector) ================================================ FILE: clojure/try-a-clojure-project-in-the-repl.md ================================================ # Try A Clojure Project In The REPL The `lein-try` plugin is a tool that makes it easy to quickly try out a clojure project in the lein repl. Given the name and version of a clojure project, it will drop you into a repl with that project loaded in. This is a great way to get the feel for the features of an unfamiliar clojure library before dropping it in as a dependency to your own project. First, add the plugin to your `~/.lein/profiles.clj` file by including the following line in the `:plugins` vector: ``` [lein-try "0.4.3"] ``` Then simply invoke the plugin with whatever project you want to try: ``` $ lein try automat ``` And to include a specific version number: ``` $ lein try automat "0.1.3" ``` ================================================ FILE: clojure/type-of-anything.md ================================================ # Type of Anything You can get the type of anything with the `type` function. Because Clojure is built on Java, many of these types may be types you recognize form Java. Boot up the repl to try some of these out: ```clojure > (type 5) ; java.lang.Long > (type 5.2) ; java.lang.Double > (type 5/4) ; clojure.lang.Ratio > (type (int 2)) ; java.lang.Integer > (type "hello, world!") ; java.lang.String > (type [1 2 3]) ; clojure.lang.PersistentVector > (type '(1 2 3)) ; clojure.lang.PersistentList ``` [source](https://aphyr.com/posts/302-clojure-from-the-ground-up-basic-types) ================================================ FILE: clojure/when-overflow-is-desired.md ================================================ # When Overflow Is Desired If you try to add two `MAX_VALUE` Longs, Clojure is kind enough to warn you. ```clojure > (+ Long/MAX_VALUE Long/MAX_VALUE) ArithmeticException integer overflow clojure.lang.Numbers.throwIntOverflow (Numbers.java:1424) ``` However, when overflow is desired, you can use the *unchecked* operators (e.g. `unchecked-add`). ```clojure > (unchecked-add Long/MAX_VALUE Long/MAX_VALUE) -2 ``` See also `unchecked-subtract` and `unchecked-multiply`. ================================================ FILE: css/add-fab-icons-to-your-site-with-fontawesome-5.md ================================================ # Add Fab Icons To Your Site With FontAwesome 5 Check out the latest version of [FontAwesome](https://fontawesome.com/). > Version 5 has been re-written and re-designed completely from scratch. One part of the rewrite is that _brand_ icons have been organized into their own collection. Whether you are using the full suite or just the brands bundle, you'll be referencing these icons with the `fab` class name. ```html ``` This will give you a React icon. Check out all the different [_brand_ icons here](https://fontawesome.com/icons?d=gallery&s=brands). ================================================ FILE: css/add-line-numbers-to-a-code-block-with-counter.md ================================================ # Add Line Numbers To A Code Block With Counter The [`counter`](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_counter_styles/Using_CSS_counters) feature in CSS is a stateful feature that allows you to increment and display a number based on elements' locations in the document. This feature is useful for adding numbers to headings and lists, but it can also be used to add line numbers to a code block. We need to initialize the counter to start using it. This will give it a name and default it to the value 0. We'll tie this to a `pre` tag which wraps our lines of code. ```css {{ title: 'globals.css' }} pre.shiki { counter-reset: line-number; } ``` Then we need to increment the counter for every line of code that appears in the code block ```css {{ title: 'globals.css' }} pre.shiki .line { counter-increment: line-number; } ``` Last, we need to display these incrementing `line-number` values _before_ each line. ```css {{ title: 'globals.css }} pre.shiki .line:not(:last-of-type)::before { content: counter(line-number); /* * plus any styling and spacing of the numbers */ } ``` This essentially attaches an element to the front (`::before`) of the line whose content is the current value of `line-number`. It is applied to all but the last `.line` because [shiki](https://shiki.matsu.io/) includes an empty `.line` at the end. Here is [the real-world example of this](https://github.com/pingdotgg/uploadthing/blob/4954c9956c141a25a5405991c34cc5ce8d990085/docs/src/styles/tailwind.css#L13-L37) that I referenced for this post. Note: the counter can be incremented, decremented, or even explicitly set to a specific value. ================================================ FILE: css/animate-smoothly-between-two-background-colors.md ================================================ # Animate Smoothly Between Two Background Colors CSS animations allow you to set up simple rules that the rendering engine can then apply to create smooth transitions between style states. To smoothly transition between two background colors, we can create a keyframes at-rule with a fitting name (e.g. `pulse`). ```css @keyframes pulse { 0% { background-color: red; } 50% { background-color: blue; } 100% { background-color: red; } } ``` Over the course of a single animation, this set of rules will start the background color at red, transition to blue 50% of the way through, and then back to red again. We can then apply this animation within any of our CSS class definitions. ```css .square1 { animation: pulse 2s infinite; width: 100px; height: 100px; } ``` Anything with a class of `square1` will have a width and height of `100px` as well as a pulsing background color. ================================================ FILE: css/apply-multiple-box-shadows-to-single-element.md ================================================ # Apply Multiple Box Shadows To Single Element Multiple box-shadows can be applied to the same DOM element. The `box-shadow` property accepts a comma-separated list of `box-shadow` directives. Here is a pretty nasty looking example from the [MDN docs](https://developer.mozilla.org/en-US/docs/Web/CSS/box-shadow#Syntax): ```css /* Any number of shadows, separated by commas */ box-shadow: 3px 3px red, -1em 0 0.4em olive; ``` Here is a more practical [example](https://codepen.io/jbranchaud/pen/GRReaxo): ```css box-shadow: 0 0 0 2px #4caf50, 0 3px 5px 0 rgba(0,0,0,0.5); ``` This gives the effect of a solid border with some shadow for depth. Multiple shadows has [pretty robust browser support](https://developer.mozilla.org/en-US/docs/Web/CSS/box-shadow#Browser_compatibility). [source](https://stackoverflow.com/questions/8556604/is-there-a-way-to-use-two-css3-box-shadows-on-one-element/8556627) ================================================ FILE: css/apply-styles-based-on-dark-mode-preferences.md ================================================ # Apply Styles Based On Dark-Mode Preferences There is a CSS media query for whether a user prefers a Dark-Mode or Light-Mode color scheme. If you're site is able to provide styling for both modes, then you can hook into those preferences like so: ```css @media (prefers-color-scheme: dark) { /* dark-mode styles */ /* perhaps changing some custom properties */ } @media (prefers-color-scheme: light) { /* light-mode styles */ /* perhaps changing some custom properties */ } ``` As of this writing, [`prefers-color-scheme`](https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-color-scheme) is still in a draft state, but it already has decent browser support. [source](https://24ways.org/2019/interactivity-and-animation-with-variable-fonts/) ================================================ FILE: css/apply-styles-to-the-last-child-of-a-specific-type.md ================================================ # Apply Styles To The Last Child Of A Specific Type The [`:last-child`](https://developer.mozilla.org/en-US/docs/Web/CSS/:last-child) pseudo-class is a way of specifying styling that will only be applied to an element if it is the last child among its siblings. What if we have elements that are declared amongst elements of another type? This can complicate that styling. The styling ```css span:last-child { color: red; } ``` won't take effect on our last `span` here ```html
One Two Three
Something unrelated
``` because amongst its siblings it isn't the last. One way of getting around this is with the [`:last-of-type`](https://developer.mozilla.org/en-US/docs/Web/CSS/:last-of-type) pseudo-class. ```css span:last-of-type { color: red; } ``` See a [live example here](https://codepen.io/jbranchaud/pen/JLEyLP). ================================================ FILE: css/change-the-orientation-of-an-image.md ================================================ # Change The Orientation Of An Image A single-line CSS transform is all it takes to change the orientation of an image (or any DOM element, really). For instance, if I have an image that is _on its side_, I can use the following [`rotate` transform](https://developer.mozilla.org/en-US/docs/Web/CSS/transform-function/rotate) to orient it correctly. ```css img { transform: rotate(90deg); } ``` It takes an angle which can be specified in degrees. Here I use `90deg`. If I was going for a different effect, I could do something like `45deg`. ================================================ FILE: css/circular-icons-with-a-massive-border-radius.md ================================================ # Circular Icons With A Massive Border Radius Elements by nature are rectangular. By adding some `border-radius`, you can soften the edges a bit. If you add a massive amount of `border-radius`, you effectively make the element round. ```css div.circle { border-radius: 50000px; } ``` Here is a [live example](https://codepen.io/jbranchaud/pen/bGGJKJW). ================================================ FILE: css/clean-up-repetition-with-is-pseudo-class.md ================================================ # Clean Up Repetition With :is() Pseudo-Class You can use the [`:is()`](https://developer.mozilla.org/en-US/docs/Web/CSS/:is) pseudo-class to match on a number of different selectors in a concise way. For instance, here is how you can make some CSS shorter and more readable: ```css /* before */ h1.text--bold, h2.text--bold, h3.text--bold, h4.text--bold, h5.text--bold { font-weight: 500; } /* after */ :is(h1, h2, h3, h4, h5).text--bold { font-weight: 500; } ``` Here is how [CSS-tricks](https://css-tricks.com/almanac/selectors/i/is/) describes it: > :is() is the current name for the "Matches-Any" pseudo-class in the CSS4 working draft. The goal of the "Matches Any" selector (with all of it's names) is to make complex groupings of selectors easier to write. It still has limited browser support, so use it sparingly. ================================================ FILE: css/conditional-styling-for-unsupported-css-features.md ================================================ # Conditional Styling For Unsupported CSS Features As much as possible, you should aim to use CSS features that have broad browser support. Sometimes browsers lag behind or you'd like to take advantage of a draft feature in browsers that support it. For these situations, you can provide conditional styling using the [`@supports` at-rule](https://developer.mozilla.org/en-US/docs/Web/CSS/@supports). Here is an example of conditionally using `display: grid` if it is supported: ```css @supports (display: grid) { div { display: grid; } } ``` In [this article](https://24ways.org/2019/zs-still-not-dead-baby-zs-still-not-dead/) there is an example of using `background-blend-mode` and falling back to `background-image` if it isn't supported. ```css @supports not (background-blend-mode: normal) { body { background-image: url(fallback.png); } } ``` ================================================ FILE: css/create-a-pulsing-background-with-css-animation.md ================================================ # Create A Pulsing Background With CSS Animation You can create a smoothly pulsing background effect with CSS animations. This can be achieved by defining a set of keyframes that start at one background color, transitions to another color, and then transitions back to the original color. ```css @keyframes pulse { 0%, 100% { background-color: #f56a3f; } 50% { background-color: #9e42b0; } } ``` At the beginning (`0%`) and end (`100%`) we declare the background color to be `#f56a3f`. Halfway through (`50%`) it should be `#9e42b0`. The browser will animate everything in between. This can then be applied infinitely with a lengthy duration to give it a real smooth feel. ```css body { animation: pulse 20s infinite; } ``` Here is a [live example](https://codepen.io/jbranchaud/pen/vYYqQjO). [source](https://css-tricks.com/almanac/properties/a/animation/) ================================================ FILE: css/define-css-custom-properties-with-scss-variables.md ================================================ # Define CSS Custom Properties With SCSS Variables It doesn't work to directly declare a CSS custom property using SCSS variables like this: ```css $primary-action: #057A5B; .btn-primary { --button-color: $primary-action; } ``` After SCSS pre-processing, the resulting CSS will look like this: ```css .btn-primary { --button-color: $primary-action; } ``` Instead of the variable being translated into its declared value (`#057A5B` in this case), it is left as is. This is because CSS custom property syntax is unusual enough that it gets processed literally. The only way to incorporate a variable is with _interpolation_. ```css $primary-action: #057A5B; .btn-primary { --button-color: #{$primary-action}; } ``` Wrapping the SCSS variable in interpolation syntax (`#{ ... }`) will do the job. [source](https://sass-lang.com/documentation/style-rules/declarations#custom-properties) ================================================ FILE: css/define-hsl-colors-with-alpha-values.md ================================================ # Define HSL Colors With Alpha Values HSL is a more intuitive option for defining colors in CSS. It is an acronym which stands for Hue, Saturation, and Lightness which are the three components of an HSL value. It is also possible to include a fourth value: Alpha for the amount of transparency of the color. The modern syntax allows you to use `hsl` with or without an alpha value, and no need to use comma separators. If the alpha value is included, it must be separated from the other three values with a forward slash. ```css .modern-hsl { background-color: hsl(340deg 100% 50%); } .modern-hsla { background-color: hsl(340deg 100% 50% / 0.75); } ``` If you need IE-support, then you'll have to use the older syntax. This version of `hsl` requires comma separators, and to apply an alpha value you have to switch to `hsla`. ```css .old-hsl { background-color: hsl(340deg, 100%, 50%); } .old-hsla { background-color: hsla(340deg, 100%, 50%, 0.75); } ``` You can see it in action and play around with the value in this [live example](https://codepen.io/jbranchaud/pen/gOLVzjx?editors=1100). See [Can I Use? on HSL](https://caniuse.com/?search=hsl) for more details on browser support. h/t to [Josh Comeau](https://twitter.com/JoshWComeau) and his excellent [CSS for JS Developers course](https://css-for-js.dev/) ================================================ FILE: css/display-responsive-iframe-maintaining-aspect-ratio.md ================================================ # Display Responsive iframe Maintaining Aspect Ratio Generally when rendering an iframe, you'll specify the `width` and `height` properties to give it a fixed display size. You can make the iframe responsively expand to the full width of its parent while maintaining its aspect ratio using a little CSS. First, remove the `width` and `height` properties. Second, add a wrapping iframe container: ```html
``` Third, sprinkle on a little CSS to make it responsive: ```css .iframe-container { position: relative; overflow: hidden; /* 16:9 aspect ratio */ padding-top: 56.25%; } .iframe-container iframe { position: absolute; width: 100%; height: 100%; top: 0; left: 0; border: 0; } ``` Ben Marshall has a whole [guide to responsive iframes](https://www.benmarshall.me/responsive-iframes/). ================================================ FILE: css/dry-up-scss-with-mixins.md ================================================ # Dry Up SCSS With Mixins If you have a similar chunk of styling that is being duplicated across your CSS, you'd probably like to dry it up to reduce the pain of maintaining it. Mixins provide one way of dealing with this problem. First, declare a named mixin of the styles that you are trying to dry up. ```css @mixin navigation { nav { display: flex; justify-content: space-around; ul { list-style: none; li a { text-decoration: none; } } } } ``` Then, this mixin can be _included_ wherever it is needed. ```css div.base-nav { @include navgation; background-color: #fff; color: #444; nav ul { li a:hover { color: #222; } } } div.admin-nav { @include navgation; background-color: #000; color: #fff; nav ul { li a:hover { color: #ddd; } } } ``` Any subsequent changes to the core navigation styling only need to be made in one place, the mixin. [source](http://sass-lang.com/guide) h/t Dorian Karter ================================================ FILE: css/filter-blur-requires-expensive-calculation.md ================================================ # Filter Blur Requires Expensive Calculation I had [a page](https://www.visualmode.dev/connect-to-production-rails-console-aws-flightcontrol) on my blog that was experiencing some odd rendering behavior. The issue was manifesting a couple ways. - Resizing and scrolling were janky and causing entire page layers to re-render causing the page to flash in and out. - Sometimes entire layer chunks would fail to paint leaving a white block missing from the page. The issue was occurring with and without JavaScript turned on for a statically-built page. I suspected that some aspect of the CSS was at fault. I was going back and forth with Dillon Hafer about what the issue could be and he wondered, "could it be the backdrop-blur class from tailwind?". I tried removing that class and the responsiveness of the page immediately improved. The [`filter: blur`](https://developer.mozilla.org/en-US/docs/Web/CSS/filter-function/blur) and [`backdrop-filter: blur`](https://developer.mozilla.org/en-US/docs/Web/CSS/backdrop-filter) both use an expensive [Gaussian blur](https://en.wikipedia.org/wiki/Gaussian_blur) calculation. One of these on a modern machine and browser probably won't have a noticable impact. However, a bunch of them, as in the case of my page with a recurring component, can have quite the performance hit. [source](https://github.com/tailwindlabs/tailwindcss/issues/15256) ================================================ FILE: css/give-elements-the-same-width-with-flexbox.md ================================================ # Give Elements The Same Width With Flexbox By default, a row of elements in a basic flex container are going to have dynamic widths that accommodate their contents. This may not be desirable if you're going for a uniform look. You could give all child elements a fixed width (e.g. `width: 100px`), but that loses out on the _flexibility_ of a flexbox layout. You can instead give all child elements a uniform and flexible width using the `flex` property. ```css .flex-container { display: flex; } .flex-child { flex: 1; } ``` This value is a relative ratio. If all children of the flex container have the same `flex` value (i.e. `1`), then they will all be equally sized and that size will adjust as the width of the flex container changes. [source](https://css-tricks.com/the-thought-process-behind-a-flexbox-layout/) ================================================ FILE: css/let-pointer-events-pass-through-an-element.md ================================================ # Let Pointer Events Pass Through An Element A graphical element can absorb a click event preventing your preferred target from recieving it. And nothing will happen. If the element is purely for visual effect, then there is some CSS you can add so that the underlying element will receive the click event. ```css .ignore-clicks { pointer-events: none; } ``` The [`pointer-events`](https://developer.mozilla.org/en-US/docs/Web/CSS/pointer-events) property specifies if and how an element "can become the target of pointer events." > In addition to indicating that the element is not the target of pointer > events, the value none instructs the pointer event to go "through" the > element and target whatever is "underneath" that element instead. [source](https://bradfrost.com/blog/post/overcomplicatin/) ================================================ FILE: css/lighten-and-darken-with-css-brightness-filter.md ================================================ # Lighten And Darken With CSS Brightness Filter CSS has a `filter` property that can be used with a variety of filter functions. One of them is the `brightness()` filter. By feeding a percentage less than `100%` to `brightness()`, the target element will be made darker. Inversely, feeding a percentage greater than `100%` to `brightness()` will make the element brighter. ```css .brighter-span { filter: brightness(150%); } .darker-span { filter: brightness(50%); } ``` ![brighter, regular, and darker spans](https://i.imgur.com/q4oy1d0.png) See this [CSS Tricks Article on the `filter` property](https://css-tricks.com/almanac/properties/f/filter/) for more details. Check out the browser support story [here](http://caniuse.com/#feat=css-filters). ================================================ FILE: css/lighten-and-darken-with-scss.md ================================================ # Lighten And Darken With SCSS With SCSS, a color can be lightened or darkened by a certain percentage using the [`lighten`](http://sass-lang.com/documentation/Sass/Script/Functions.html#lighten-instance_method) and [`darken`](http://sass-lang.com/documentation/Sass/Script/Functions.html#darken-instance_method) functions, respectively. For instance, given the following HTML ```html
``` I can style `div.two` with the original color and then style `div.one` with a lightened version and `div.three` with a darkened version. ```scss $box-color: #0074d9; .two { background: $box-color; } .one { background: lighten($box-color, 20%); } .three { background: darken($box-color, 20%); } ``` The result looks something like this: ![](http://i.imgur.com/SaeTL8H.png) ================================================ FILE: css/make-a-block-of-text-respect-new-lines.md ================================================ # Make A Block Of Text Respect New Lines Generally when we fill a `div` tag full of text, it will display it one long strand irrespective of any line breaks that are included. This is a great default, but not necessarily what we want when we are displaying text from another source, such as our users. We can convince a block of text to respect new lines by adding a couple CSS properties. ```css .multiline-text { word-wrap: break-word; white-space: pre-line; } ``` The first rule, `word-wrap: break-word`, ensures that long lines of text uninterrupted by new lines respect the boundaries of our wrapping element. The second rule, `white-space: pre-line`, handles squashing of extra white space and respecting of new lines. See a [working example here](https://codepen.io/anon/pen/bQpKyv). ================================================ FILE: css/parameterized-scss-mixins.md ================================================ # Parameterized SCSS Mixins A mixin can be made to be much more versatile by parameterizing it. If you need variations of a block of CSS, then move the parts that vary out into parameters to the mixin. ```css @mixin navigation($background-color, $color, $link-color) { nav { display: flex; justify-content: space-around; background-color: $background-color; color: $color; ul { list-style: none; li a { text-decoration: none; color: $link-color; } } } } div.base-nav { @include navgation(#fff, #444, #222); } div.admin-nav { @include navgation(#000, #fff, #ddd); } ``` The mixin can now easily be used to customize different segments of your app's styling. ================================================ FILE: css/prevent-invisible-elements-from-being-clicked.md ================================================ # Prevent Invisible Elements From Being Clicked I have a nav element that when clicked reveals a custom drop-down menu. It reveals it using CSS transitions and transformations (`opacity` and `scale`). When the nav element is clicked again, the reverse of these transformations is applied to "hide" the menu. This gives a nice visual effect. It only makes the menu invisible and doesn't actually make it go away. That means that menu could be invisible, but hovering over the top of a button on the screen. The button cannot be clicked now because the menu is intercepting that [_pointer event_](https://developer.mozilla.org/en-US/docs/Web/CSS/pointer-events). The fix is to apply CSS (or a class) when the drop-down menu is closed that tells it to ignore _pointer events_. ```css .pointer-events-none { pointer-events: none; } ``` This is more of less what [the `pointer-events-none` TailwindCSS utility](https://tailwindcss.com/docs/pointer-events) looks like. This class is applied by default to the drop-down menu. Then when the nav item is clicked, some JavaScript removes that class at the same moment that the menu is visually appearing. When a menu item is selected or the menu otherwise closed, it transitions away and the `pointer-events-none` class is reapplied. ================================================ FILE: css/root-has-higher-specificity-than-html.md ================================================ # :root Has Higher Specificity Than html The `:root` CSS pseudo-selector and `html` will target the same element -- ``, but `:root` has higher specificity. That means the property rules declared under `:root` will take precedence over those under `html`. ```css :root { background: red; } html { background: blue; } ``` In the case of the above code, the `` element's background color will be `red`. [source](https://developer.mozilla.org/en-US/docs/Web/CSS/:root) ================================================ FILE: css/style-a-background-with-a-linear-gradient.md ================================================ # Style A Background With A Linear Gradient The [`linear-gradient`](https://developer.mozilla.org/en-US/docs/Web/CSS/linear-gradient) function in its simplest form can be used to style the background of an element with a vertical, linear gradient between two colors. ![gradient example](https://i.imgur.com/Jybn4MB.png) See the Pen pQpypW by Josh Branchaud (@jbranchaud) on CodePen. Here is what the CSS looks like: ```css .container { background: linear-gradient(#00449e, #e66465); } ``` The background of any element with the `container` class will be styled with a linear gradient that transitions from `#00449e` to `#e66465`. ================================================ FILE: css/using-maps-in-scss.md ================================================ # Using Maps In SCSS You can collect a set of like values into a [map](https://sass-lang.com/documentation/values/maps) with the following SCSS syntax: ```scss $backgrounds: ( "gray": #AAAAAA, "red": #FF4136, "blue": #0074D9, ); ``` You can then access a value from the map using the [`map-get` function](https://sass-lang.com/documentation/values/maps#look-up-a-value): ```scss .boxA { background-color: map-get($backgrounds, "gray"); } .boxB { background-color: map-get($backgrounds, "red"); } .boxC { background-color: map-get($backgrounds, "blue"); } ``` See a [live example here](https://codepen.io/jbranchaud/pen/WVJrgp). ================================================ FILE: cursor/allow-cursor-to-be-launched-from-cli.md ================================================ # Allow Cursor To Be Launched From CLI It is nice to be able to open Cursor for a specific project directly from the terminal like so: ```bash $ cd ~/dev/my/project $ cursor . ``` For the `cursor` launcher binary to be available like that, we have to find it and add it to the path. It is probably located in the `/Applications` folder and within that nested down a couple directories is a `bin` directory that contains the binary we're looking for. ```bash ls /Applications/Cursor.app/Contents/Resources/app/bin  bin/ ├──  code* ├──  cursor* └──  cursor-tunnel* ``` The `cursor` binary is what we want, so let's add that to our path. In my case, I'll add this to my `~/.zshrc` file. ```bash export PATH="/Applications/Cursor.app/Contents/Resources/app/bin:$PATH" ``` ================================================ FILE: deno/read-in-the-contents-of-a-file.md ================================================ # Read In The Contents Of A File Deno offers some nice utilities out of the box like reading in the contents of a file from the filesystem. The `readTextFile` function is available on the `Deno` object. ```typescript // Read a file using Deno const text: string = await Deno.readTextFile("./first_input.txt"); ``` You use a top-level await with the function call and, assuming the file exists, it will read the contents in. In this case, I assign them to the `text` variable. For the file reading to work when the program is executed, you must use the `--allow-read` flag. ```bash $ deno run --allow-read program.ts ``` [source](https://deno.land/manual@v1.14.0/examples/read_write_files) ================================================ FILE: devops/aliasing-an-ansible-host.md ================================================ # Aliasing An Ansible Host When specifying the hosts that Ansible can interact with in the `/etc/ansible/hosts` file, you can put just the IP address of the host server, like so: ```yml 192.168.1.50 ``` IP addresses are not particularly meaningful for a person to look at though. Giving it a name serves as better documentation and makes it easier to see what host servers are in play during a task. Ansible makes it easy to alias host servers. For example, we can name our host `staging` like so: ```yml staging ansible_host=192.168.1.50 ``` [source](https://docs.ansible.com/ansible/latest/inventory_guide/intro_inventory.html#inventory-aliases) ================================================ FILE: devops/allow-cross-origin-requests-to-include-cookies.md ================================================ # Allow Cross-Origin Requests To Include Cookies When making a cross-origin fetch request from a client (e.g. browser) to a server, all kinds of CORS protections are enforced by the browser. One of those protections, by default, is to avoid XSS attacks by not sending credentials (e.g. cookies, authorization headers or TLS client certificates) in the request or expose credentials to the client JavaScript code. This is controlled by the [Access-Control-Allow-Credentials](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Credentials) header. If we want to include things like cookies in the request, then we need to have both the client-originating request and the server to agree to allow credentials. The client-side fetch will need to specify that credentials should be included: ```javascript fetch(url, { credentials: 'include' }) ``` The server, either in response to a GET or a preflight request, will need to do two things. First, the response headers need to have `Access-Control-Allow-Credentials` set to `true`. Second, the [`Access-Control-Allow-Origin`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Origin) will need to name the specific origin (the client). In other words, the allowed origin cannot be set to `*`. [source](https://stackoverflow.com/a/24689738/535590) ================================================ FILE: devops/allow-https-through-your-ufw-firewall.md ================================================ # Allow HTTPS Through Your UFW Firewall UFW -- Uncomplicated Firewall -- is just what is sounds like. I have it running on a DigitalOcean box and it is only letting through traffic on ports 80 (HTTP) and 22 (SSH). I am setting up SSL for a domain hosted on this box which means I need to also let through traffic on 443 (HTTPS). The allowed ports can be checked with the `status` command: ```bash $ sudo ufw status Status: active To Action From -- ------ ---- OpenSSH ALLOW Anywhere Nginx HTTP ALLOW Anywhere OpenSSH (v6) ALLOW Anywhere (v6) Nginx HTTP (v6) ALLOW Anywhere (v6) ``` As we can see, `HTTPS` has not yet been allowed by `ufw`. We can _allow_ HTTPS traffic with the `allow` command. ```bash $ sudo ufw allow https ``` Check the status again and see that `HTTPS` is now included in the list. [source](https://www.digitalocean.com/community/tutorials/how-to-setup-a-firewall-with-ufw-on-an-ubuntu-and-debian-cloud-server) h/t Dillon Hafer ================================================ FILE: devops/check-for-cached-site-association-file-for-ios.md ================================================ # Check For Cached Site Assocation File For iOS Apple caches your [AASA (Apple App Site Association)](https://developer.apple.com/documentation/Xcode/supporting-associated-domains) file periodically. This is used for signaling what site URLs should be Univeral Links to your iOS app. You can view the contents of the cached site association file by inserting your domain into the end of this URL -- `https://app-site-association.cdn-apple.com/a/v1/[domain]`. For instance, if my app's domain is `www.my-app.com`, then I'd pull up `https://app-site-association.cdn-apple.com/a/v1/www.my-app.com` to see what is cached. Additionally, you can run your site association file through a validator by entering your domain into the form at [AASA Validator](https://branch.io/resources/aasa-validator/). [source](https://david.y4ng.fr/implementing-and-testing-universal-links/) ================================================ FILE: devops/check-the-status-of-all-services.md ================================================ # Check The Status Of All Services In a Linux environment, you can quickly check the status of a number of different services. By running `[sudo] service --status-all`, the status command will be invoked for all services under the `/etc/init.d/` directory. So, if you want to check the status of something like `nginx` or `apache`, just run `service --status-all` and find it in the list. The `-` symbol means it isn't running, the `+` symbol means it is up, and the `?` symbol means that it cannot determine the status. ================================================ FILE: devops/check-the-syntax-of-nginx-files.md ================================================ # Check The Syntax Of nginx Files The syntax of [`nginx`](https://www.nginx.com/) configuration files can be a bit finicky. On top of that, some `nginx` server commands can fail silently. Get more confidence by using the following command to check for syntax errors in those files: ```bash $ [sudo] nginx -t ``` If there is an error, you might see something like this: ```bash $ sudo nginx -t nginx: [emerg] unexpected ";" in /etc/nginx/nginx.conf:16 nginx: configuration file /etc/nginx/nginx.conf test failed ``` If all looks good, then you'll see this: ```bash $ sudo nginx -t nginx: the configuration file /etc/nginx/nginx.conf syntax is ok nginx: configuration file /etc/nginx/nginx.conf test is successful ``` ================================================ FILE: devops/connect-to-an-rds-postgresql-database.md ================================================ # Connect To An RDS PostgreSQL Database You can connect to an RDS PostgreSQL database remotely via `psql`. First, you need to tell AWS that connections from your IP address are allowed. This is done by configuring the VPC (Virtual Private Cloud) that is associated with the RDS instance. You'll need to add an inbound rule to the Security Group associated with that VPC. You'll add an inbound rule that allows a Postgres connection on port 5432 from your IP address -- which is identified by a [CIDR address](https://www.digitalocean.com/community/tutorials/understanding-ip-addresses-subnets-and-cidr-notation-for-networking#cidr-notation). ![create inbound rule](https://i.imgur.com/Ypb9Du7.png) Once this rule has been added to the security groups associated with the VPC that is associated with your RDS instance, you'll be able to connect from your machine with `psql`. ```bash $ psql my-rds-endpoint.cbazqrhkmyo.us-east-1.rds.amazonaws.com \ --port 5432 \ --user rdsusername \ postgres ``` Assuming the database username is `rdsusername` and the specific database is named `postgres`, you'll be prompted for that user's password and then connected. ================================================ FILE: devops/default-rails-deploy-script-on-hatchbox.md ================================================ # Default Rails Deploy Script On Hatchbox I deployed a Rails app to [Hatchbox](https://hatchbox.io) recently. When following along in the log during a deploy, I can see most of what is happening as part of the deploy. Though it is too verbose to look through every line. I'd rather see the contents of the deploy script. I did quite a bit of digging around while SSH'd into my hatchbox server, but I couldn't find if or where that file might be stored. Instead, there is a [_Help Center_ article](https://hatchbox.relationkit.io/articles/55-what-is-the-default-rails-deploy-script) where Chris Oliver shares what is in the script. ```bash bundle install -j $(nproc) yarn install bundle exec rails assets:precompile [[ -n "${CRON}" ]] && bundle exec rails db:migrate ``` It does a parallelized `bundle install`, then a `yarn install` (make sure your project is using `yarn.lock`), Rails asset precompilation, and then if `CRON` is set (Cron role is available by checking _Cron_ under _Server Responsibilities_ for your Hatchbox server), it will run Rails migrations. From app settings, the deploy script can be overridden, or pre- and post-deploy steps can be added. ================================================ FILE: devops/determine-the-ip-address-of-a-domain.md ================================================ # Determine The IP Address Of A Domain The `dig` (domain information grouper) command can be used to get more information about a domain name. To discover the IP address for a given domain, invoke `dig` with the domain as an argument. ```bash $ dig joshbranchaud.com ; <<>> DiG 9.8.3-P1 <<>> joshbranchaud.com ;; global options: +cmd ;; Got answer: ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 26589 ;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0 ;; QUESTION SECTION: ;joshbranchaud.com. IN A ;; ANSWER SECTION: joshbranchaud.com. 86400 IN A 198.74.60.157 ;; Query time: 118 msec ;; SERVER: 75.75.75.75#53(75.75.75.75) ;; WHEN: Sun Jan 17 22:32:18 2016 ;; MSG SIZE rcvd: 51 ``` The *answer section* tells me that the IP address for `joshbranchaud.com` is `198.74.60.157`. See `man dig` for more details. ================================================ FILE: devops/hatchbox-exports-env-vars-with-asdf.md ================================================ # Hatchbox Exports Env Vars With asdf When you add env vars through the [Hatchbox](https://hatchbox.io/) UI, they get exported to the environment of the asdf-shimmed processes. This is handled by the [`asdf-vars` plugin](https://github.com/excid3/asdf-vars). That plugin looks for `.asdf-vars` in the current chain of directories. I can see there are many `.asdf-vars` files: ```bash $ find . -name ".asdf-vars" -type f ./.asdf-vars ./my-app/.asdf-vars ./my-app/releases/20250120195106/.asdf-vars ./my-app/releases/20250121041054/.asdf-vars ``` And it is the one in my app's directory that contains the env vars that I set in the UI. ```bash $ cat my-app/.asdf-vars BUNDLE_WITHOUT=development:test DATABASE_URL=postgresql://user_123:123456789012345@10.0.1.1/my_app_db PORT=9000 RACK_ENV=production RAILS_ENV=production RAILS_LOG_TO_STDOUT=true RAILS_MASTER_KEY=abc123 SECRET_KEY_BASE=abc123efg456 ``` When I run a shimmed process like `ruby`, those env vars are loaded into the process's environment. ```bash $ cd my-app/current $ which ruby /home/deploy/.asdf/shims/ruby $ ruby -e "puts ENV['DATABASE_URL']" postgresql://user_123:123456789012345@10.0.1.1/my_app_db ``` [source](https://www.visualmode.dev/hatchbox-manages-env-vars-with-asdf) ================================================ FILE: devops/path-of-the-packets.md ================================================ # Path Of The Packets You can use `traceroute` to determine the network path packets follow from your machine to some host server. For instance, if I want to know path between my laptop (connected to airport wifi) and my website [joshbranchaud.com](http://joshbranchaud.com), I can run the following command: ``` $ traceroute joshbranchaud.com traceroute to joshbranchaud.com (198.74.60.157), 64 hops max, 52 byte packets 1 hotspot.boingohotspot.net (192.168.144.1) 1.209 ms 1.427 ms 1.080 ms 2 192.168.13.1 (192.168.13.1) 5.272 ms 3.545 ms 6.231 ms 3 10.106.144.1 (10.106.144.1) 13.671 ms 15.311 ms 12.895 ms 4 68.13.10.184 (68.13.10.184) 14.789 ms 12.980 ms 13.628 ms 5 68.13.8.221 (68.13.8.221) 19.922 ms 12.611 ms 17.853 ms 6 nyrkbprj01-ae3.0.rd.ny.cox.net (68.1.5.157) 48.917 ms 56.641 ms 51.771 ms 7 eqix.e-2-3.tbr2.ewr.nac.net (198.32.118.157) 62.977 ms 52.970 ms 48.667 ms 8 0.e1-2.tbr2.mmu.nac.net (209.123.10.114) 66.253 ms 57.311 ms 52.242 ms 9 207.99.53.46 (207.99.53.46) 57.478 ms 53.443 ms 54.510 ms 10 joshbranchaud.com (198.74.60.157) 59.853 ms 56.375 ms 55.393 ms ``` See `man traceroute` for more details. ================================================ FILE: devops/push-non-master-branch-to-heroku.md ================================================ # Push Non-master Branch To Heroku When using git to deploy your app to Heroku, it is expected that you push to the `master` branch. When you run the following command ``` $ git push heroku master ``` Heroku will attempt to build and run your app. However, if you have a `staging` branch for your application that you want to push to your staging environment on Heroku, you cannot simply run ``` $ git push heroku staging ``` Heroku will only perform a build on pushes to the remote `master` branch. You can get around this, though, by specifying that your `staging` branch should be pushed to the remote `master` branch, like so ``` $ git push heroku staging:master ``` [source](https://coderwall.com/p/1xforw/make-heroku-run-a-non-master-branch) ================================================ FILE: devops/reload-the-nginx-configuration.md ================================================ # Reload The nginx Configuration If you've modified or replaced the configuration file, nginx will not immediately start using the updated nginx configuration. Once a `restart` or `reload` signal is received by nginx, it will apply the changes. The `reload` signal ``` $ service nginx reload ``` tells nginx to reload the configuration. It will check the validity of the configuration file and then spawn new worker processes for the latest configuration. It then sends requests to shut down the old worker processes. This means that during a *reload* nginx is always up and processing requests. [source](http://nginx.org/en/docs/beginners_guide.html) ================================================ FILE: devops/resolve-the-public-ip-of-a-url.md ================================================ # Resolve The Public IP Of A URL The `dig` command is a utility for doing DNS lookups. You can run it with a URL argument to lookup the public IP for that domain. ```bash $ dig joshbranchaud.com ; <<>> DiG 9.8.3-P1 <<>> joshbranchaud.com ;; global options: +cmd ;; Got answer: ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 62836 ;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0 ;; QUESTION SECTION: ;joshbranchaud.com. IN A ;; ANSWER SECTION: joshbranchaud.com. 1800 IN A 159.203.106.229 ;; Query time: 50 msec ;; SERVER: 75.75.75.75#53(75.75.75.75) ;; WHEN: Sun Apr 14 12:34:52 2019 ;; MSG SIZE rcvd: 51 ``` The output is a bit noisy, but if you parse down to the _ANSWER SECTION_, you'll see the IP address that it resolves to. Alternatively, you can skip the noise and get right to the IP address by including the `+short` flag. ```bash $ dig joshbranchaud.com +short 159.203.106.229 ``` See `man dig` for more details. ================================================ FILE: devops/running-out-of-inode-space.md ================================================ # Running Out Of inode Space Unix systems have two types of storage limitations. The first, and more common, is a limitation on physical storage used for storing the contents of files. The second is a limitation on `inode` space which represents file location and other data. Though it is uncommon, it is possible to run out of `inode` space before running out of disk space (run `df` and `df -i` to see the levels of each). When this happens, the system will complain that there is `No space left on device`. Both `inode` space and disk space are needed to create a new file. How can this happen? If lots of directories with lots of empty, small, or duplicate files are being created, then the `inode` space can be used up disproportionately to the amount of respective disk space. You'll need to clean up some of those files before you can continue. Sources: [this](http://blog.scoutapp.com/articles/2014/10/08/understanding-disk-inodes) and [this](http://www.linux.org/threads/intro-to-inodes.4130/) ================================================ FILE: devops/set-get-and-unset-env-vars-with-dokku.md ================================================ # Set, Get, And Unset Env Vars With Dokku The `dokku` CLI provides `config` subcommands for managing environment variables for the target container. An env var can be set for an active container with `config:set`: ```bash $ dokku config:set app-name JEMALLOC_ENABLED=true MALLOC_CONF="stats_print:true" ``` Notice I'm able to set multiple env vars at once if needed. If I ever need to check what an env var is currently set to for one of my app containers, I can use `config:get`: ```bash $ dokku config:get app-name JEMALLOC_ENABLED true ``` I can always override any value with another `config:set`. However, if I need to entirely remove the env var, I can use `config:unset`: ```bash $ dokku config:unset app-name MALLOC_CONF ``` [source](https://dokku.com/docs/configuration/environment-variables/) ================================================ FILE: devops/set-up-domain-for-hatchbox-rails-app.md ================================================ # Set Up Domain For Hatchbox Rails App When we deploy a Rails app with [Hatchbox](https://hatchbox.io), we are given an internal URL for publicly accessing our app. It is something like `https://123abc.hatchboxapp.com`. That's useful as we are getting things up and running, but eventually we want to point our own domain at the app. The first step is to tell Hatchbox what domain we are going to use. From our app's _Domain & SSL_ page we can enter a domain into the _Add A Domain_ input. For instance, I have the [visualmode.dev](https://visualmode.dev) domain and I want the [still.visualmode.dev](https://still.visualmode.dev) subdomain pointing at my Rails app. I submit the full name `still.visualmode.dev` and I get an _A Record_ ipv4 address (e.g. `23.12.234.82`). The second step is to configure a DNS record with our domain registrar. From the DNS settings of our registrar (e.g. Cloudflare) we can add an _A Record_ where we specify the name (e.g. `still`) and then include the ipv4 address provided by Hatchbox. We can save this and wait a minute for it to propagate. And soon enough we can visit our Rails app at the custom domain. ================================================ FILE: devops/ssh-into-a-docker-container.md ================================================ # SSH Into A Docker Container Before you can SSH into a Docker container, you need to know the name of the container. Run ```bash $ docker ps ``` to list the currently running containers and find the name of the one you want to connect to. If you don't see the one you are looking for, it may not be running. Then, you can connect to it by name with a bash session like so: ```bash $ docker exec -it /bin/bash ``` The [`-it`](http://docs.docker.oeynet.com/engine/reference/commandline/container_exec/) flags open an interactive emulated terminal connection. `/bin/bash` is the command that gets run in that context. [source](https://phase2.github.io/devtools/common-tasks/ssh-into-a-container/) ================================================ FILE: devops/ssl-certificates-can-cover-multiple-domains.md ================================================ # SSL Certificates Can Cover Multiple Domains When registering an SSL certificate, you can list multiple domains to be covered by it. One use case is registering a certificate that covers both the apex domain (`example.com`) and all subdomains using a wildcard (`*.example.com`). The wildcard alone will not cover `example.com`. If you want `example.com`, `www.example.com`, `blog.example.com`, etc. covered under the same certificate, then you'll need to include both the apex and wildcard domains. ================================================ FILE: devops/wipe-a-heroku-postgres-database.md ================================================ # Wipe A Heroku Postgres Database If you have some sort of non-production version of an application running on Heroku, you may encounter a time when you need to wipe your database and start fresh. For a rails project, it may be tempting to do `heroku run rake db:drop db:setup`. Heroku doesn't want you to accidentally do anything stupid, so it prevents you from running `rake db:drop`. Instead, you must send a more explicit command ``` $ heroku pg:reset DATABASE_URL ``` Heroku will ask you to confirm that you indeed want to wipe out the database and will then proceed. For the curious reader, running `heroku config` will list the values of a number of variables including `DATABASE_URL`. ================================================ FILE: docker/check-postgres-version-running-in-docker-container.md ================================================ # Check Postgres Version Running In Docker Container I have a docker container that I'm using to run a PostgreSQL development database on my local machine. It was a while ago when I set it up, so I can't remember specifically which major version of PostgreSQL I am using. I use `docker ps` to list the names of each container. ```bash $ docker ps --format "{{.Names}}" still-postgres-1 better_reads-postgres-1 ``` I grab the one I am interested in. In this case, that is `still-postgres-1`. Then I can execute a `select version()` statement with `psql` against the container with that name like so: ```bash $ docker exec still-postgres-1 psql -U postgres -c "select version()"; version --------------------------------------------------------------------------------------------------------------------- PostgreSQL 16.2 (Debian 16.2-1.pgdg120+2) on x86_64-pc-linux-gnu, compiled by gcc (Debian 12.2.0-14) 12.2.0, 64-bit (1 row) ``` And there I have it. I'm running Postgres v16 in this container. ================================================ FILE: docker/configure-different-host-and-container-ports.md ================================================ # Configure Different Host And Container Ports A `docker-compose.yml` file that sets up something like a PostgreSQL service will proxy a port from your host machine to a port on the docker container. A basic PostgreSQL service will look like this tying `5432` to `5432` under the `ports` section. ```yaml version: "3.7" services: postgres: image: postgres:latest restart: always environment: - POSTGRES_USER=postgres - POSTGRES_PASSWORD=postgres - POSTGRES_DB=postgres ports: - "5432:5432" volumes: - ./postgres-data:/var/lib/postgresql/data ``` Requests like queries from a `psql` instance that we send to `localhost:5432` will be proxied to `docker-container:5432`. Since those numbers are the same on both sides, it's not necessarily clear which is which. The left is the _host_ and the right is the _container_ -- `[host-port]:[container-port]`. If you need to use a port other than 5432 on your host machine (e.g. maybe you're running multiple Postgres servers at once), then you can just change the port number on the left side. How about `9876:5432`. ================================================ FILE: docker/list-running-docker-containers.md ================================================ # List Running Docker Containers The `docker` CLI has a `ps` command that will list all running container by default. When I run it, I can see that I have a container running a Postgres database and another running a MySQL database. ```bash $ docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES ba792e185734 postgres:latest "docker-entrypoint.s…" 12 days ago Up 12 days 0.0.0.0:9876->5432/tcp better_reads-postgres-1 7ca7c1e882e0 mysql:8.0 "docker-entrypoint.s…" 19 months ago Up 8 seconds 33060/tcp, 0.0.0.0:3309->3306/tcp some-app-db-1 ``` It lists several pieces of info about the containers: the container id, the image it is based off, when it was created, the running status, the port configuration, and the name of the container If I run `docker ps --help` I can see some additional options. One option is the `--all` flag which will display all known docker container instead of just the running ones. ================================================ FILE: docker/prevent-containers-from-running-on-startup.md ================================================ # Prevent Containers From Running On Startup I have a bunch of docker containers managed by Docker Desktop. Some are related to projects I'm actively working on. Whereas many others are inactive projects. When I restart my machine, regardless of which containers I had running or turned off, several of them are booted into a running state on startup. This is becaue their restart policy is set to `always`. That's fine for the project I'm actively working on, but the others I would like to be _off_ by default. I need to update each of their restart policies from `always` to `no`. First, I need to figure out their container IDs: ```bash $ docker ps --all CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES eb7b40aeba2d postgres:latest "docker-entrypoint.s…" 3 months ago Up 11 minutes 0.0.0.0:9875->5432/tcp still-postgres-1 eb9ab2213f2b postgres:latest "docker-entrypoint.s…" 3 months ago Exited (0) 11 minutes ago next-drizzle-migration-repro-app-postgres-1 ba792e185734 postgres:latest "docker-entrypoint.s…" 4 months ago Up 11 minutes 0.0.0.0:9876->5432/tcp better_reads-postgres-1 3139f9beae76 postgres:latest "docker-entrypoint.s…" 9 months ago Exited (128) 7 months ago basic-next-prisma-postgres-1 ``` Referencing the `CONTAINER ID` and `NAMES` columns, I'm able to then inspect each container and see the current `RestartPolicy`: ```bash $ docker inspect eb9ab2213f2b | grep -A3 RestartPolicy "RestartPolicy": { "Name": "always", "MaximumRetryCount": 0 }, ``` I can then update the `RestartPolicy` to be `no`: ```bash $ docker update --restart no eb9ab2213f2b ``` Inpsecting that container again, I can see the updated policy: ```bash $ docker inspect eb9ab2213f2b | grep -A3 RestartPolicy "RestartPolicy": { "Name": "no", "MaximumRetryCount": 0 }, ``` Rinse and repeat for each of the offending containers. [source](https://stackoverflow.com/questions/45423334/stopping-docker-containers-from-being-there-on-startup) ================================================ FILE: docker/run-a-basic-postgresql-server-in-docker.md ================================================ # Run A Basic PostgreSQL Server In Docker Here is a basic `docker-compose.yml` file for spinning up a Docker container that runs a PostgreSQL server on port 5432. This is what I use to create a locally-running PostgreSQL server that lives inside a docker container. ```yaml version: "3.7" services: postgres: image: postgres:latest restart: always environment: - POSTGRES_USER=postgres - POSTGRES_PASSWORD=postgres - POSTGRES_DB=postgres ports: - "5432:5432" volumes: - ./postgres-data:/var/lib/postgresql/data ``` To create the docker container and start it up, run the following command from the same directory where you put this file: ```bash $ docker compose up ``` This command knows to look for the `docker-compose.yml` file though you can always be explicit about the file with the `-f` option. This configuration points at `postgres:latest` which currently is `16.1`. To run a different major version, you can change the `image` to something like `postgres:15`. See [Docker Hub](https://hub.docker.com/_/postgres) for more options. ================================================ FILE: docker/run-sql-script-against-postgres-container.md ================================================ # Run SQL Script Against Postgres Container I've been using dockerized Postgres for local development with several projects lately. This is typically with framework tooling (like Rails) where schema migrations and query execution are handled by the tooling using the specified connection parameters. However, I was experimenting with and iterating on some Postgres functions outside of any framework tooling. I needed a way to run the SQL script that (re)creates the function via `psql` on the docker container. With a local, non-containerized Postgres instance, I'd redirect the file to `psql` like so: ```bash $ psql -U postgres -d postgres < experimental-functions.sql ``` When I tried doing this with `docker exec` though, it was silently failing / doing nothing. As far as I can tell, there was a mismatch with redirection handling across the bounds of the container. To get around this, I first copy the file into the `/tmp` directory on the container: ```bash $ docker cp experimental-functions.sql still-postgres-1:/tmp/experimental-functions.sql ``` Then the `psql` command that docker executes can be pointed directly at a local-to-it SQL file. ```bash $ docker exec still-postgres-1 psql \ -U postgres \ -d postgres \ -f /tmp/experimental-functions.sql ``` There are probably other ways to handle this, but I got into a nice rhythm with this file full of `create or replace function ...` definitions where I could modify, copy over, execute, run some SQL to verify, and repeat. ================================================ FILE: dprint.json ================================================ { "excludes": ["README.md"], "plugins": ["https://plugins.dprint.dev/markdown-0.16.0.wasm"] } ================================================ FILE: drizzle/create-bigint-identity-column-for-primary-key.md ================================================ # Create bigint Identity Column For Primary Key Using the Drizzle ORM with Postgres, here is how we can create a table that uses a [`bigint` data type](https://orm.drizzle.team/docs/column-types/pg#bigint) as a primary key [identity column](https://www.postgresql.org/docs/current/ddl-identity-columns.html). ```typescript import { pgTable, bigint, text, timestamp, } from "drizzle-orm/pg-core"; // Users table export const users = pgTable("users", { id: bigint({ mode: 'bigint' }).primaryKey().generatedAlwaysAsIdentity(), email: text("email").unique().notNull(), name: text("name").notNull(), createdAt: timestamp("created_at").defaultNow().notNull(), }); ``` There are a couple key pieces here: 1. We import `bigint` so that we can declare a column of that type. 2. We specify that it is a primary key with `.primaryKey()`. 3. We declare its default value as `generated always as identity` via `.generatedAlwaysAsIdentity()`. Note: you need to specify the `mode` for `bigint` or else you will see a `TypeError: Cannot read properties of undefined (reading 'mode')` error. If we run `npx drizzle-kit generate` the SQL migration file that gets generated will contain something like this: ```sql --> statement-breakpoint CREATE TABLE IF NOT EXISTS "users" ( "id" bigint PRIMARY KEY GENERATED ALWAYS AS IDENTITY (sequence name "users_id_seq" INCREMENT BY 1 MINVALUE 1 MAXVALUE 9223372036854775807 START WITH 1 CACHE 1), "email" text NOT NULL, "name" text NOT NULL, "created_at" timestamp DEFAULT now() NOT NULL, CONSTRAINT "users_email_unique" UNIQUE("email") ); ``` ================================================ FILE: drizzle/drizzle-tracks-migrations-in-a-log-table.md ================================================ # Drizzle Tracks Migrations In A Log Table When I generate (`npx drizzle-kit generate`) and apply (`npx drizzle-kit migrate`) schema migrations against my database with Drizzle, there are SQL files that get created and run. How does Drizzle know which SQL files have been run and which haven't? Like many SQL schema migration tools, it uses a table in the database to record this metadata. Drizzle defaults to calling this table `__drizzle_migrations` and puts it in the `drizzle` schema (which is like a database namespace). Let's take a look at this table for a project with two migrations: ```sql postgres> \d drizzle.__drizzle_migrations Table "drizzle.__drizzle_migrations" Column | Type | Collation | Nullable | Default ------------+---------+-----------+----------+---------------------------------------------------------- id | integer | | not null | nextval('drizzle.__drizzle_migrations_id_seq'::regclass) hash | text | | not null | created_at | bigint | | | Indexes: "__drizzle_migrations_pkey" PRIMARY KEY, btree (id) postgres> select * from drizzle.__drizzle_migrations; id | hash | created_at ----+------------------------------------------------------------------+--------------- 1 | 8961353bf66f9b3fe1a715f6ea9d9ef2bc65697bb8a5c2569df939a61e72a318 | 1730219291288 2 | b75e61451e2ce37d831608b1bc9231bf3af09e0ab54bf169be117de9d4ff6805 | 1730224013018 (2 rows) ``` Notice that Drizzle stores each migration record as [a SHA256 hash of the migration file](https://github.com/drizzle-team/drizzle-orm/blob/526996bd2ea20d5b1a0d65e743b47e23329d441c/drizzle-orm/src/migrator.ts#L52) and a timestamp of when the migration was run. [source](https://orm.drizzle.team/docs/drizzle-kit-migrate#applied-migrations-log-in-the-database) ================================================ FILE: drizzle/get-fields-for-inserted-row.md ================================================ # Get Fields For Inserted Row With Drizzle, we can insert a row with a set of values like so: ```typescript await db .insert(todoItems) .values({ title, userId, description, }) ``` The result of this is `QueryResult`. In other words, nothing useful is coming back to us from the database. Sometimes an insert is treated as a fire-and-forget (as long as it succeeds) or since we know what data we are inserting, we don't need the database to response. But what about values that are generated or computed by the database -- such as an id from a sequence, timestamp columns that default to `now()`, or generated columns. To get all the fields of a freshly inserted row, we can tack on [the `returning()` function](https://orm.drizzle.team/docs/insert#insert-returning) (which likely adds something like [`returning *`](https://www.postgresql.org/docs/current/dml-returning.html)) to the insert query under the hood). ```typescript await db .insert(todoItems) .values({ title, userId, description, }) .returning() ``` This will have a return type of `Array` which means that for each inserted row we'll have all the fields (columns) for that row. Alternatively, if we just need the generated ID for the new row(s), we can use a partial return like so: ```typescript await db .insert(todoItems) .values({ title, userId, description, }) .returning({ id: todoItems.id }) ``` ================================================ FILE: elixir/all-values-for-a-key-in-a-keyword-list.md ================================================ # All Values For A Key In A Keyword List A keyword list in Elixir can contain the same key multiple times. ```elixir kwl = [a: 1, b: 2, a: 3, c: 4] #=> [a: 1, b: 2, a: 3, c: 4] ``` The `get/2` function will only grab the value of the first occurrence. ```elixir Keyword.get(kwl, :a) #=> 1 ``` You can use `get_values/2` to retrieve _all_ values associated with the key. ```elixir Keyword.get_values(kwl, :a) #=> [1, 3] ``` ================================================ FILE: elixir/append-to-a-keyword-list.md ================================================ # Append To A Keyword List If you have two keyword lists, you can append them like so: ```elixir > a = [a: 1] [a: 1] > b = [b: 2] [b: 2] > a ++ b [a: 1, b: 2] ``` But what if something a bit more programmatic is happening and you are building up the additions to the keyword list based on variables? ```elixir > x = :x :x > c = a ++ [x 5] ** (CompileError) iex:5: undefined function x/1 (stdlib) lists.erl:1353: :lists.mapfoldl/3 (stdlib) lists.erl:1354: :lists.mapfoldl/3 ``` That makes elixir think `x` is some function when in fact it is just a variable containing the keyword `:x`. Simply adding a comma doesn't quite do it either. ```elixir > c = a ++ [x, 5] [{:a, 1}, :x, 5] ``` We need to wrap the internal part with curly braces to create the tuple that can then be appended to `a`. ```elixir > c = a ++ [{x, 5}] [a: 1, x: 5] ``` ================================================ FILE: elixir/assert-an-exception-is-raised.md ================================================ # Assert An Exception Is Raised Elixir's [`ExUnit`](http://elixir-lang.org/docs.html) comes with a number of different ways to make assertions in your tests. One of those functions is [`assert_raise`](http://elixir-lang.org/docs/stable/ex_unit/ExUnit.Assertions.html#assert_raise/2) which allows you to test that a particular exception is raised when the given function is invoked. Using `assert_raise/2` looks something like this: ```elixir assert_raise FunctionClauseError, fn -> Enum.chunk([1,2,3], 0) end ``` The `assert_raise/3` form is also available which allows you to test both the type of exception and the resulting message. ```elixir assert_raise FunctionClauseError, ~r/^no function clause matching/, fn -> Enum.chunk([1,2,3], 0) end ``` Using the regex sigil for the second argument is generally a good way to go to keep tests from getting too brittle. ================================================ FILE: elixir/binary-representation-of-a-string.md ================================================ # Binary Representation Of A String > A common trick in Elixir is to concatenate the null byte <<0>> to a string > to see its inner binary representation. A couple example of this can be seen in the following snippet of code: ```elixir > "hello" <> <<0>> <<104, 101, 108, 108, 111, 0>> > "ƒå®øü†" <> <<0>> <<198, 146, 195, 165, 194, 174, 195, 184, 195, 188, 226, 128, 160, 0>> ``` [source](http://elixir-lang.org/getting-started/binaries-strings-and-char-lists.html) ================================================ FILE: elixir/check-for-a-substring-match.md ================================================ # Check For A Substring Match Using Erlang's `:binary.match` function, you can easily check if a string has a matching substring. ```elixir > :binary.match("all food is good", "foo") {4, 3} > :binary.match("all food is good", "bar") :nomatch ``` As you can see, the return value on a successful match is a tuple with the index of where the match starts and the length of the match. If there is no match, the `:nomatch` atom is returned. See the [`match/2` and `match/3` docs](http://erlang.org/doc/man/binary.html#match-2) for more details. [source](http://stackoverflow.com/questions/35551072/how-to-find-index-of-a-substring) ================================================ FILE: elixir/check-list-membership.md ================================================ # Check List Membership You can use the [`in` operator](https://hexdocs.pm/elixir/operators.html) to check if something appears in a list. This is a handy way of checking if a variable is one of a few acceptable or expected values. For instance, a common DateTime comparison pattern relies on this to check if a DateTime is `>=` or `<=` to another DateTime. ```elixir {:ok, datetime} = DateTime.from_naive(~N[2016-05-24 13:26:08.003], "Etc/UTC") DateTime.compare(datetime, DateTime.utc_now()) in [:lt, :eq] ``` Alternatively, you can check that something does not have membership in a list by also including the `not` operator. ```elixir DateTime.compare(datetime, DateTime.utc_now()) not in [:lt, :eq] ``` ================================================ FILE: elixir/comparing-datetime-structs.md ================================================ # Comparing DateTime Structs > Remember, comparisons in Elixir using ==/2, >/2, structural and based on the DateTime struct fields. For proper comparison > between datetimes, use the compare/2 function. As the [DateTime docs](https://hexdocs.pm/elixir/DateTime.html) say, you'll want to use [`compare/2`](https://hexdocs.pm/elixir/DateTime.html#compare/2) in order to accurately compare two `DateTime` structs. ```elixir {:ok, older} = DateTime.from_naive(~N[2016-05-24 13:26:08.003], "Etc/UTC") {:ok, newer} = DateTime.from_naive(~N[2017-11-24 13:26:08.003], "Etc/UTC") DateTime.compare(older, newer) #=> :lt DateTime.compare(newer, older) #=> :gt DateTime.compare(newer, newer) #=> :eq ``` When using `compare/2`, you'll get one of `:lt`, `:gt`, or `:eq` as a result, meaning _less than_, _greater than_, or _equal_ respectively. ================================================ FILE: elixir/compute-intermediate-values-in-a-with-construct.md ================================================ # Compute Intermediate Values In A With Construct The expressions you use in a `with` construct do not have to contain the `<-` syntax. You can pattern match and bind values along the way as well. ```elixir with %{id: id} <- get_user(), url = "/api/#{id}/blogs", %{status_code: 200, body: body} <- HTTPoison.get(url), {:ok, decoded_body} <- Poison.decode(body) do {:ok, decoded_body} end ``` In the above (sorta contrived) example we were able to construct a URL in the middle of the series of expressions. The values we compute inline will be closed into the `with` construct, so they won't leak. See the [`with` docs](https://hexdocs.pm/elixir/Kernel.SpecialForms.html#with/1) for more details. ================================================ FILE: elixir/compute-md5-digest-of-a-string.md ================================================ # Compute md5 Digest Of A String To compute the md5 digest of a string, we can use Erlang's top-level `md5` function. ```elixir > :erlang.md5("#myelixirstatus") <<145, 148, 139, 99, 194, 176, 105, 18, 242, 246, 37, 69, 142, 69, 226, 199>> ``` This, however, gives us the result in the raw binary representation. We would like it in a base 16 encoding, as md5 digests tend to be. We can wrap (or pipe) this with `Base.encode16` to get the result we are looking for. ```elixir > Base.encode16(:erlang.md5("#myelixirstatus"), case: :lower) "91948b63c2b06912f2f625458e45e2c7" ``` [source](https://gist.github.com/10nin/5713366#gistcomment-1445219) ================================================ FILE: elixir/counting-records-with-ecto.md ================================================ # Counting Records With Ecto Sometimes you want to know how many records there are in a table. Ecto gives us a couple ways to approach this. We can use the [`count\1`](https://hexdocs.pm/ecto/Ecto.Query.API.html#count/1) function that the Ecto query API provides. ```elixir > Repo.one(from p in "people", select: count(p.id)) 16:09:52.759 [debug] QUERY OK source="people" db=1.6ms SELECT count(p0."id") FROM "people" AS p0 [] 168 ``` Alternatively, we can use the [`fragment/1`](https://hexdocs.pm/ecto/Ecto.Query.API.html#fragment/1) function to use PostgreSQL's `count` function. ```elixir > Repo.one(from p in "people", select: fragment("count(*)")) 16:11:19.818 [debug] QUERY OK source="people" db=1.5ms SELECT count(*) FROM "people" AS p0 [] 168 ``` Lastly, `Ecto.Repo` has the [`aggregate/4`](https://hexdocs.pm/ecto/Ecto.Repo.html#c:aggregate/4) function which provides a `:count` option. ```elixir > Repo.aggregate(from(p in "people"), :count, :id) 16:11:23.786 [debug] QUERY OK source="people" db=1.7ms SELECT count(p0."id") FROM "people" AS p0 [] 168 ``` ================================================ FILE: elixir/create-a-date-with-the-date-sigil.md ================================================ # Create A Date With The Date Sigil Elixir 1.3 introduced a new sigil for creating dates, `~D`. It works in the same way as Date's [`new/3`](http://elixir-lang.org/docs/stable/elixir/Date.html#new/3) function producing the Date struct with each of the date parts. ```elixir > ~D[2016-01-01] ~D[2016-01-01] > ~D[2016-01-01].year 2016 > ~D[2016-01-01].month 1 > ~D[2016-01-01].day 1 ``` ================================================ FILE: elixir/create-a-list-of-atoms.md ================================================ # Create A List Of Atoms The `~w` sigil makes it easy to create a word list -- a list of strings -- where each word is separated by a space. ```elixir > ~w(bulbasaur charmander squirtle) ["bulbasaur", "charmander", "squirtle"] ``` By appending an `a` onto that sigil construct, you are instructing Elixir that you would instead like a list of atoms. ```elixir > ~w(bulbasaur charmander squirtle)a [:bulbasaur, :charmander, :squirtle] ``` [source](http://elixir-lang.org/getting-started/sigils.html#strings) ================================================ FILE: elixir/creating-a-pid.md ================================================ # Creating A PID Often times, when invoking a function that spawns a process, the PID of the spawned process is returned and we bind to it. That PID is a reference to some BEAM process in our system. We can create our own references using the `pid/3` function. Let's assume we have the following processes, among others, in our system at the moment. ```elixir > Process.list |> Enum.reverse |> Enum.take(3) [#PID<0.284.0>, #PID<0.283.0>, #PID<0.282.0>] ``` We can create a reference to any of them using the three number parts that they are made up of. ```elixir > pid(0, 284, 0) #PID<0.284.0> ``` See, it's alive. ```elixir > pid(0, 284, 0) |> Process.alive? true ``` What if we make up a PID that doesn't actually reference any process? ```elixir > pid(0, 333, 0) |> Process.alive? false ``` Note: there is also a `pid/1` version of the function. See `h pid` for more details. ================================================ FILE: elixir/creating-indexes-with-ecto.md ================================================ # Creating Indexes With Ecto Using indexes in the right places within relational databases is a great way to speed up queries. To add a basic index in an Ecto migration, use `Ecto.Migration.index\2`: ```elixir create index(:users, [:email]) ``` Creating a composite index doesn't require jumping through any hoops; just put the relevant column names in the list: ```elixir create index(:posts, [:user_id, :title]) ``` See `h Ecto.Migration.index` for more details. ================================================ FILE: elixir/defining-multiple-clauses-in-an-anonymous-function.md ================================================ # Defining Multiple Clauses In An Anonymous Function Anonymous functions often take the approach of doing a single thing with the inputs, regardless of their shape or values. There is no need to limit ourselves though. The same pattern matching that we use all over our Elixir programs can be utilized to define multiple clauses in an anonymous function as well. Consider the following example: ```elixir iex> my_function = fn {:ok, x} -> "Everything is ok: #{x}" {:error, x} -> "There was an error: #{x}" end #Function<6.52032458/1 in :erl_eval.expr/5> ``` We can then invoke our anonymous function using the bound variable to see what results we get with different kinds of inputs. ```elixir iex> my_function.({:ok, 123}) "Everything is ok: 123" iex> my_function.({:error, "be warned"}) "There was an error: be warned" ``` [source](http://stackoverflow.com/a/18023790/535590) ================================================ FILE: elixir/determine-the-latest-release-of-a-hex-package.md ================================================ # Determine The Latest Release Of A Hex Package I will often pop open the browser and do a Google search in order to figure out the latest release of a package when adding it to my dependencies. However, lately I've been getting in the habit of using a quicker approach. The `mix` CLI has a way of looking up info about a package and we don't have to leave the terminal to use it. For instance, if I need to determine the latest version of the `postgrex` package, I can run the following command. ```bash $ mix hex.info postgrex PostgreSQL driver for Elixir. Config: {:postgrex, "~> 0.12.0"} Releases: 0.12.0, 0.11.2, 0.11.1, 0.11.0, 0.10.0, 0.9.1, 0.9.0, 0.8.4, 0.8.3, 0.8.2, ... Maintainers: Eric Meadows-Jönsson, James Fish Licenses: Apache 2.0 Links: Github: https://github.com/elixir-ecto/postgrex ``` The third line gives me the info I need (`{:postgrex, "~> 0.12.0"}`) and it is already formatted as a tuple that I can paste right into my `mix.exs` file. ================================================ FILE: elixir/do-you-have-the-time-part-2.md ================================================ # Do You Have The Time? - Part 2 In [_Do You Have The Time?_](https://github.com/jbranchaud/til/blob/master/elixir/do-you-have-the-time.md), I demonstrated a way of using an Erlang function to get at and work with time in Elixir. As of Elixir 1.3, there is now a [`Time` module](http://elixir-lang.org/docs/stable/elixir/Time.html) that provides a sigil and some functions for working with time. We can use Elixir's `Time` module to simplify the example from the previous iteration of this TIL: ```elixir defmodule TickTock do def current_time do Time.from_erl!(:erlang.time) |> Time.to_string end end > TickTock.current_time "19:58:12" ``` ================================================ FILE: elixir/do-you-have-the-time.md ================================================ # Do You Have The Time? Elixir doesn't come with any standard ways of getting at or working with time. There are packages like [Timex](https://github.com/bitwalker/timex) out there that we can pull in to our projects. However, if we don't have need for a full-featured date/time library, we can opt for a simpler solution. Erlang can give us the time. ```elixir defmodule TickTock do def current_time do {hh,mm,ss} = :erlang.time "#{hh}:#{mm}:#{ss}" end end > TickTock.current_time "11:47:13" ``` ================================================ FILE: elixir/documentation-lookup-with-vim-and-alchemist.md ================================================ # Documentation Lookup With Vim And Alchemist _Which argument position is the accumulator for `Enum.reduce/3`?_ _How does `group_by` work?_ I find myself fairly frequently jumping from vim to Chrome to do Google searches for Elixir standard lib documentation. It gets the job done, but it is kinda slow and I'd prefer to avoid the context switch. With [alchemist.vim](https://github.com/slashmili/alchemist.vim), Elixir documentation lookup is at your finger tips. Just move the cursor over the module or function you are curious about and hit `K` (from normal mode). Curious about `Enum.reduce`? Type it out in your current Vim buffer, move the cursor over it, and hit `K`. ================================================ FILE: elixir/dynamically-generating-atoms.md ================================================ # Dynamically Generating Atoms > Atoms are constants where their name is their own value. The use of atoms like `:ok` and `:error` show up all over the place in Elixir. These are atoms that tend to be statically defined. Atoms can also be dynamically defined using string interpolation. For example, I can generate a handful of atoms by mapping over a range of integers. ```elixir > Enum.map(1..5, &(:"some_atom_#{&1}")) [:some_atom_1, :some_atom_2, :some_atom_3, :some_atom_4, :some_atom_5] ``` ================================================ FILE: elixir/execute-raw-sql-in-an-ecto-migration.md ================================================ # Execute Raw SQL In An Ecto Migration If you are performing a database migration with [Ecto](https://hexdocs.pm/ecto/Ecto.html), perhaps the most straightforward approach is to use Ecto's DSL. However, the DSL may not always be the best choice. Being able to write raw SQL gives you more control. It will also enable you to use database features that may not be directly or easily available through the DSL. Raw SQL can be included in a Ecto migration with a combination of Elixir's heredoc syntax and the [`Ecto.Migration#execute/1` function](https://hexdocs.pm/ecto/Ecto.Migration.html#execute/1). You'll also need to provide both an `up` and `down` function to ensure that your migrations are reversible. ```elixir defmodule MyApp.Repo.Migrations.CreatePostsTable do use Ecto.Migration def up do execute """ create table posts ( id serial primary key, title varchar not null, body varchar not null default '', inserted_at timestamptz not null default now(), updated_at timestamptz not null default now() ); """ end def down do execute "drop table posts;" end end ``` ================================================ FILE: elixir/expose-internal-representation.md ================================================ # Expose Internal Representation Elixir is a language that has strong support for metaprogramming. It provides easy access to an internal representation of the code in the form of an Abstract Syntax Tree (AST) using maps and keyword lists. The `quote` macro is used to expose this internal representation. ```elixir > quote do: 2 * 2 {:*, [context: Elixir, import: Kernel], [2, 2]} > quote do: 2 * 2 == 4 {:==, [context: Elixir, import: Kernel], [{:*, [context: Elixir, import: Kernel], [2, 2]}, 4]} ``` [source](http://elixir-lang.org/getting-started/meta/quote-and-unquote.html) ================================================ FILE: elixir/include-captures-with-string-split.md ================================================ # Include Captures With String.split The [`String.split/3`](http://elixir-lang.org/docs/stable/elixir/String.html#split/3) function comes with two options: `trim` and `parts`. However, when using it with a regex pattern, you gain access to a couple extra options, including `include_captures`. This is because when used with a regex pattern, `String.split` just invokes `Regex.split` which comes with extra options like `include_captures`. Here is `String.split` in action by itself and with the supported `trim` option. ```elixir > String.split("23d", ~r/\d+/) ["", "d"] > String.split("23d", ~r/\d+/, trim: true) ["d"] ``` Adding in the `include_captures` option, we get a resulting list that includes the value captured by the splitting regex. ``` > String.split("23d", ~r/\d+/, trim: true, include_captures: true) ["23", "d"] ``` This isn't a documented feature, so it is only supported as long as `Regex.split` supports it and as long as `String.split` continues to delegate to `Regex.split`. h/t Chris Erin ================================================ FILE: elixir/inspecting-the-process-message-queue.md ================================================ # Inspecting The Process Message Queue A core tenant of Elixir is message passing between processes. So, if a process is sent a message, where does that message go? What happens if it gets sent many messages? The `Process.info/2` function allows us to inspect the message queue. First, let's send some messages (to ourself) and then keep an eye on the length of the message queue as we go. ```elixir > send self(), {:error, "this is bad"} {:error, "this is bad"} > Process.info(self(), :message_queue_len) {:message_queue_len, 1} > send self(), {:hello, "world"} {:hello, "world"} > Process.info(self(), :message_queue_len) {:message_queue_len, 2} ``` Now, I am curious what those specific messages are. Let's ask `Process.info/2` for the messages that are in the message queue. ```elixir > Process.info(self(), :messages) {:messages, [error: "this is bad", hello: "world"]} ``` There are a lot of other things that `Process.info/2` can tell us about a process. See [the Erlang docs for `process_info`](http://erlang.org/doc/man/erlang.html#process_info-2) for more details. ================================================ FILE: elixir/list-functions-for-a-module.md ================================================ # List Functions For A Module During an `iex` session, I can do a little introspection on modules using either the [`__info__/1`](http://elixir-lang.org/docs/stable/elixir/Module.html#__info__/1) function or Erlang's [`module_info/0`](http://erlang.org/doc/man/erlang.html#module_info-0) function. In particular, I can pass `:functions` to either one to get a list of the functions for that module. This is what `__info__/1` looks like for the functions of the `List` module: ```elixir > List.__info__(:functions) [delete: 2, delete_at: 2, duplicate: 2, first: 1, flatten: 1, flatten: 2, foldl: 3, foldr: 3, insert_at: 3, keydelete: 3, keyfind: 3, keyfind: 4, keymember?: 3, keyreplace: 4, keysort: 2, keystore: 4, keytake: 3, last: 1, replace_at: 3, to_atom: 1, to_existing_atom: 1, to_float: 1, to_integer: 1, to_integer: 2, to_string: 1, to_tuple: 1, update_at: 3, wrap: 1, zip: 1] ``` [source](http://stackoverflow.com/questions/28664119/in-elixir-is-there-any-way-to-get-a-module-to-list-its-functions) ================================================ FILE: elixir/listing-files-in-iex.md ================================================ # Listing Files In IEx When you start an IEx session, you do so in the context of some directory -- the current working directory. This context can be important if you need to do something like import a file. In fact, you may want to know what files are available in the current working directory. You can list them all out within IEx using `ls/0`. ```elixir iex(1)> ls() .git .gitignore README.md _build assets config deps lib mix.exs mix.lock priv test tmp ``` You can also list the contents of some other specific directory by naming it when invoking `ls/1`. See `h()` within IEx for more details. ================================================ FILE: elixir/match-on-a-map-in-a-with-construct.md ================================================ # Match On A Map In A With Construct Many usage example of the [`with`](https://hexdocs.pm/elixir/Kernel.SpecialForms.html#with/1) construct show a series of matches on a tuple. ```elixir with {:ok, width} <- Map.fetch(opts, :width), {:ok, height} <- Map.fetch(opts, :height) do {:ok, width * height} end ``` You can match on more than just tuples though. Here is how you might match on a map. ```elixir with %{status_code: 200, body: body} <- HTTPoison.get!(url), {:ok, decoded_body} <- Poison.decode(body) do {:ok, decoded_body} end ``` In fact, you have the full power of Elixir's pattern matching available to you in your series of matches for a `with` construct. ================================================ FILE: elixir/passing-around-and-using-modules.md ================================================ # Passing Around And Using Modules A module is a bag of functions. When we define a module, we are tying it to an atom. If we pass around the atom that references this module, then we can use it to call its functions. For example, consider two types of greetings: ```elixir defmodule Hello do def get_greeting do "Hello, World!" end end defmodule Hola do def get_greeting do "Hola, Mundo!" end end ``` And a generic greeting module that accepts a language module: ```elixir defmodule Greeting do def say_hello(language_module) do language_module.get_greeting |> IO.puts end end Greeting.say_hello(Hello) # => "Hello, World!" Greeting.say_hello(Hola) # => "Hola, Mundo!" ``` The module reference that we pass in to `Greeting.say_hello` can be used to invoke the `get_greeting` function. ================================================ FILE: elixir/pattern-matching-in-anonymous-functions.md ================================================ # Pattern Matching In Anonymous Functions Pattern matching shows up everywhere in Elixir, even where you may not be expecting it. When declaring an anonymous function, you can use pattern matching against different sets and shapes of input parameters to invoke different behaviors. Here is an example of how you might use this: ```elixir > handle_result = fn {:ok, result} -> IO.puts "The result is #{result}" :error -> IO.puts "Error: couldn't find anything" end #Function<6.50752066/1 in :erl_eval.expr/5> > Map.fetch(%{a: 1}, :a) |> handle_result.() The result is 1 :ok > Map.fetch(%{a: 1}, :b) |> handle_result.() Error: couldn't find anything :ok ``` [source](https://elixirschool.com/lessons/basics/functions#pattern-matching) ================================================ FILE: elixir/pipe-into-a-case-statement.md ================================================ # Pipe Into A Case Statement The standard use of a case statement looks something like this: ```elixir case HTTPoison.get(url) do {:ok, %HTTPoison.Response{status_code: 200, body: body}} -> IO.puts body {:ok, %HTTPoison.Response{status_code: 404}} -> IO.puts "Not found :(" {:error, %HTTPoison.Error{reason: reason}} -> IO.inspect reason end ``` If you are a fan of the pipe syntax, then you may enjoying writing the above like this: ```elixir url |> HTTPoison.get() |> case do {:ok, %HTTPoison.Response{status_code: 200, body: body}} -> IO.puts body {:ok, %HTTPoison.Response{status_code: 404}} -> IO.puts "Not found :(" {:error, %HTTPoison.Error{reason: reason}} -> IO.inspect reason end ``` Just like any function, the value from the previous line in the pipe will be passed in and used as the value switched over in the case statement. ================================================ FILE: elixir/quitting-iex.md ================================================ # Quitting IEx There are two ways to quit out of an Interactive Elixir shell. The standard way is with `Ctrl-c`. This gives you a list of options, one of which is `a` for _abort_. This will terminate your IEx session and drop you back on the command line where the process started. Additionally, IEx also understands `Ctrl-\` which is control key that will terminate just about any interactive environment. This command will cause IEx to immediately exit with no prompt. Note: IEx does not, however, respond to `Ctrl-d`. [source](http://blog.plataformatec.com.br/2016/03/how-to-quit-the-elixir-shell-iex/) ================================================ FILE: elixir/range-into-list-using-comprehensions.md ================================================ # Range Into List Using Comprehensions Using an _identity_ comprehension and the `:into` option, we can easily convert a range into a list. ```elixir > for x <- 1..10, into: [], do: x [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] ``` Check out [the docs for the `:into` option](http://elixir-lang.org/getting-started/comprehensions.html#the-into-option) for more details. ================================================ FILE: elixir/refer-to-a-module-within-itself.md ================================================ # Refer To A Module Within Itself Elixir comes with the `__MODULE__` reserve word for referencing a module within itself. This is handy for things like structs. ```elixir defmodule SomeNamespace.MyModule do defstruct [:id] def do_thing(%__MODULE__{}=thing) do # ... end end ``` You can use an alias in order to ditch `__MODULE__` and perhaps make your code a bit more human readable. ```elixir defmodule SomeNamespace.MyModule do alias __MODULE__, as: MyModule defstruct [:id] def do_thing(%MyModule{}=thing) do # ... end end ``` ================================================ FILE: elixir/referencing-values-in-iexs-history.md ================================================ # Referencing Values In IEx's History Each time we execute a statement in an `iex` session, the counter is incremented. These numbers are references to the history of the session. We can use these references to _refer_ to previously executed values using `v/1`. This is particularly handy for multi-line statements or when we forget to bind to the result of some function. Consider the following `iex` session: ```elixir iex(1)> :one :one iex(2)> 1 + 1 2 iex(3)> "three" |> String.to_atom() :three ``` If we execute `v()` on its own, it is the same as `v(-1)` in that it will give us the latest value in the history. ```elixir iex(4)> v() :three ``` Providing any positive number will refer to the references we see next to each statement. ```elixir iex(5)> v(1) :one ``` Negative numbers, as we saw with `v(-1)`, will count backwards in the history from where we are. ```elixir iex(6)> v(-4) 2 ``` See `h v` for more details. ================================================ FILE: elixir/remove-one-list-from-another.md ================================================ # Remove One List From Another The `--/2` operator allows you to subtract two lists, that is, remove all elements in the _right_ list from the _left_ list. Each occurrence of an element is removed if there is a corresponding element. If there is no corresponding element, it is ignored. Here are some examples. ```elixir > [1, 2, 3] -- [2, 4] [1, 3] > [:a, :b, :c, :a, :d, :a] -- [:a, :a] [:b, :c, :d, :a] ``` This kind of list operation is not particularly efficient, so for large lists it can be quite slow. The following example took several minutes to run. ```elixir > Enum.into(1..1000000, []) -- Enum.into(2..1000000, []) [1] ``` To achieve a true set difference, you'll note that the docs for this operator recommend checking out `MapSet.difference/2`. See `h Kernel.--` for more details. ================================================ FILE: elixir/replace-duplicates-in-a-keyword-list.md ================================================ # Replace Duplicates In A Keyword List Use the [`Keyword.put`](http://elixir-lang.org/docs/stable/elixir/Keyword.html#put/3) function to replace duplicate key entries in a keyword list. If there are no duplicate entries, the entry will just be added. ```elixir Keyword.put([a: 1], :b, 2) [b: 2, a: 1] ``` If there is a duplicate entry, it will be replaced by the new value. ```elixir > Keyword.put([b: 1, a: 1], :b, 2) [b: 2, a: 1] ``` If there are multiple duplicate entries, they will all be replaced. ```elixir > Keyword.put([b: 3, b: 4, a: 1], :b, 2) [b: 2, a: 1] ``` ================================================ FILE: elixir/requiring-keys-for-structs.md ================================================ # Requiring Keys For Structs When defining a [`struct`](http://elixir-lang.org/getting-started/structs.html) in Elixir, we may want to ensure that certain values are provided. We can require that certain keys are used in the creation of a struct with the [`@enforce_keys`](https://hexdocs.pm/elixir/Kernel.html#defstruct/1) attribute. Here is an example of a `Name` struct. ```elixir defmodule Name do @enforce_keys [:first, :last] defstruct [:first, :middle, :last] end ``` With this defined, we can create a struct that uses all of the keys. ```elixir > jack = %Name{first: "Jack", middle: "Francis", last: "Donaghy"} %Name{first: "Jack", last: "Donaghy", middle: "Francis"} ``` We can also ignore `:middle` and just provide the required keys. ```elixir > liz = %Name{first: "Liz", last: "Lemon"} %Name{first: "Liz", last: "Lemon", middle: nil} ``` We cannot, however, omit any of the keys specified in `@enforce_keys`. If we do omit any of them, Elixir will raise an error. ``` elixir > tracy = %Name{first: "Tracy"} ** (ArgumentError) the following keys must also be given when building struct Name: [:last] expanding struct: Name.__struct__/1 iex:6: (file) ``` ================================================ FILE: elixir/reversing-a-list-part-2.md ================================================ # Reversing A List - Part 2 In [Reversing A List](reversing-a-list.md), I showed how Erlang's `:lists.reverse()` function could be used to reverse a list. Since then, Elixir now has a built-in function for reversing lists. In fact, it works with anything that implements the `Enumerable` protocol. ```elixir > Enum.reverse([1,2,3,4,5]) [5, 4, 3, 2, 1] > Enum.reverse(%{a: 1, b: 2, c: 3}) [c: 3, b: 2, a: 1] ``` ================================================ FILE: elixir/reversing-a-list.md ================================================ # Reversing A List To efficiently work with and _transform_ lists in Elixir, you will likely need utilize a list reversing function from time to time. Your best bet is to reach for the Erlang implementation which is available as part of the `lists` module. Here are a couple examples of how to use it: ```elixir > :lists.reverse([1,2,3]) [3, 2, 1] > :lists.reverse([1, :a, true, "what", 5]) [5, "what", true, :a, 1] ``` Note: though I said "_transform_ lists" above, what is actually going on is that a new version of the list representing my transformation is being created, per Elixir's functional nature. ================================================ FILE: elixir/root-directory-of-a-project.md ================================================ # Root Directory Of A Project Do you need the root directory of an elixir project? The [`File.cwd!/0`](http://elixir-lang.org/docs/stable/elixir/File.html#cwd!/0) function can help. ```elixir iex> File.cwd! "/home/dev/code/my_app" ``` Keep in mind though, this will only work reliably with projects that are compiled using Mix. [source](https://groups.google.com/forum/#!msg/elixir-lang-talk/Ls0eJDdMMW8/1Lmg5K2MAQAJ) ================================================ FILE: elixir/round-floats-to-integers.md ================================================ # Round Floats To Integers The [`Float.round/2`](http://elixir-lang.org/docs/stable/elixir/Float.html#round/2) function allows you to round floating point numbers. The result is another floating point number. ```elixir > Float.round(1.5) 2.0 > Float.round(1.3) 1.0 ``` If, instead, you always want an integer as the result of rounding, you can use `Kernel`'s version of a rounding function: ```elixir > Kernel.round(1.5) 2 > Kernel.round(1.3) 1 ``` The [`Kernel.round/1`](http://elixir-lang.org/docs/stable/elixir/Kernel.html#round/1) function accepts both integers and floats, and always returns an integer. Additionally, it can be used in guard tests. ================================================ FILE: elixir/run-exunit-tests-in-a-deterministic-order.md ================================================ # Run ExUnit Tests In A Deterministic Order When running a file of [`ExUnit`](http://elixir-lang.org/docs/stable/ex_unit/ExUnit.html) tests, they will be executed in a pseudo-random order based on a seed value. In general, `ExUnit` will pick a random seed each time you run your tests. If you'd like to add some determinism to the order that your tests run in, you can specify the seed in the `ExUnit` configuration. ```elixir ExUnit.configure seed: 42 ExUnit.start defmodule AssertionTest do use ExUnit.Case, async: true test "the truth" do assert true end test "more truth" do assert 2 + 2 = 4 end end ``` [source](https://til.hashrocket.com/posts/4a7dc5fd94-run-exunit-tests-in-the-order-they-are-defined) h/t Chris Erin ================================================ FILE: elixir/run-the-test-at-a-specific-line-number.md ================================================ # Run The Test At A Specific Line Number You can tell `mix test` to only run tests that appear in a specific file by naming the file: ```bash $ mix test test/module/file_test.exs ``` You can even point it to a specific line number in that file like so: ```bash $ mix test test/module/file_test.exs:45 ``` Behind the scenes, `mix test` is using tags to build a list of exclusions and inclusions that result in only the test at line 45 running. Here is an equivalent of the above command: ```bash $ mix test test/module/file_test.exs --exclude test --include line:45 ``` This will exclude every test. It will then re-include the test that corresponds to having a tag `line:45`. ================================================ FILE: elixir/same-functions-should-be-grouped-together.md ================================================ # Same Functions Should Be Grouped Together A favorite feature of Elixir is the function clauses that can be defined in multiple ways with pattern matching. I've always grouped same-named function clauses together. It seems like good form and it's what I see everyone else doing. It also makes for readable, maintainable code. This is more than just personal preference though. It is the correct, idiomatic way to organize your Elixir function clauses. The compiler will let you know if anything gets out of place. Consider the following snippet of code: ```elixir defmodule MeterToLengthConverter do def convert(:feet, m), do: m * 3.28084 def convert(:inch, m), do: m * 39.3701 def hello(), do: IO.puts "Hello, World!" def convert(:yard, m), do: m * 1.09361 end ``` It is syntactically correct, so it will compile. However, the compiler will emit a warning like the following: ``` warning: clauses for the same def should be grouped together, def convert/2 was previously defined (length_converter.ex:2) length_converter.ex:5 ``` ================================================ FILE: elixir/skip-a-specific-test.md ================================================ # Skip A Specific Test Elixir's `ExUnit` supports all kinds of tags for customizing the behavior of your test suite. The `:skip` tag signals that `ExUnit` should skip over that specific test. ```elixir @tag :skip test "this is a test that should not run" do assert false end ``` When you run `mix test`, it will make sure to not execute any tests with the `:skip` tag. Using the `:skip` tag can be a great temporary measure in a lot of situations. It is not a good long-term practice to keep these around and check them in. ================================================ FILE: elixir/string-interpolation-with-just-about-anything.md ================================================ # String Interpolation With Just About Anything Coming to Elixir from Ruby, I am used to being able to interpolate literally _anything_ into a string. In Elixir, this is not the case. > By default, it handles strings, atoms (including nil, true, false and > module name aliases like String – which are all just atoms behind the > scenes), integers, floats, and some lists. That's it. There are two approaches you can take to interpolate everything else into a string. The easier approach is to use [`Kernel.inspect/2`](http://elixir-lang.org/docs/stable/elixir/Kernel.html#inspect/2). ```elixir > IO.puts "A map #{inspect %{a: 1, b: 2}}" A map %{a: 1, b: 2} ``` The other approach is to implement the `String.Chars` protocol for the thing that you are trying to print. You can read more about that in [_Elixir String Interpolation for Rubyists_](http://thepugautomatic.com/2016/01/elixir-string-interpolation-for-the-rubyist/). ================================================ FILE: elixir/unique-indexes-with-ecto.md ================================================ # Unique Indexes With Ecto You can create a unique index in a migration for one or more columns using the [`unique_index/3`](https://hexdocs.pm/ecto/Ecto.Migration.html#unique_index/3) function. For example, if you are creating a join table for `followers` and want to ensure that duplicate _follower_ entries are prevented, you may want to include a unique index like so: ```elixir create table(:followers) do add :followed_user, references(:users), null: false add :following_user, references(:users), null: false end create unique_index(:followers, [:followed_user, :following_user]) ``` Keep in mind that `unique_index/3` is a shorthand for [`index/3`](https://hexdocs.pm/ecto/Ecto.Migration.html#index/3) when you set `unique: true`. ================================================ FILE: elixir/updating-values-in-a-map.md ================================================ # Updating Values In A Map When working with maps in any language, you often need a way to update key-value pairs. Furthermore, you will need a way to handle keys that are not already present in the map, generally associating some default value. In Elixir, the `Map` module provides the `get_and_update/3` function as a way of accomplishing such a task. You need to provide the map, the key to be fetched and updated, and a function for _transforming_ (or updating) the existing value. Let's use a score counting example to see it in action: ```elixir > scores = %{} %{} # jake scores a point > {_, scores} = Map.get_and_update(scores, :jake, fn(x) -> {x, (x || 0) + 1} end) {nil, %{jake: 1}} # chris scores a point > {_, scores} = Map.get_and_update(scores, :chris, fn(x) -> {x, (x || 0) + 1} end) {nil, %{chris: 1, jake: 1}} # jake scores another point > {_, scores} = Map.get_and_update(scores, :jake, fn(x) -> {x, (x || 0) + 1} end) {1, %{chris: 1, jake: 2}} # final scores > scores %{chris: 1, jake: 2} ``` We use `(x || 0) + 1` as the updated value. This is a simple way of providing a default value for when the key is not already present in the map. The update function is expected to return a tuple with the _original_ value and the _updated_ value. See [the docs](http://elixir-lang.org/docs/stable/elixir/Map.html#get_and_update/3) for more details. ================================================ FILE: elixir/using-when-clauses-in-a-with-construct.md ================================================ # Using When Clauses In A With Construct Because Elixir's `with` construct supports the full power of the language's pattern matching, we can use `when` clauses to further narrow down our matches. For instance, if we want to match against the response to an API request, but only for response status codes in the 2xx range, we can do something like the following: ```elixir with %{status_code: code, body: body} when code >= 200 && code < 300 <- HTTPoison.get!(url), {:ok, decoded_body} <- Poison.decode(body) do {:ok, decoded_body} end ``` See the [docs for `with`](https://hexdocs.pm/elixir/Kernel.SpecialForms.html#with/1) for more details. ================================================ FILE: elixir/virtual-fields-with-ecto-schemas.md ================================================ # Virtual Fields With Ecto Schemas If you'd like to include a particular key-value pair in an Ecto changeset, it needs to be included as a field in the schema. In the case of something akin to a password field, you want to be able to perform validations against it, but the password itself does not have a column in the database. In other words, you want to use the password in memory as part of the validation process but not save it to the database. To accomplish this, you need to specify that it is a `virtual` field. ```elixir schema "users" do field :username, :string field :password_digest, :string field :password, :string, virtual: true end ``` With that schema, you can then validate the `:password` and transform it into the corresponding `:password_digest` field. ```elixir def registration_changeset(model, params) do model |> changeset(params) # do other standard validations |> cast(params, [:password]) # include :password in the changeset |> validate_length(:password, min: 8) # validations |> put_pass_hash() # transform into :password_digest end ``` ================================================ FILE: elixir/when-things-dont-match-the-with-statements.md ================================================ # When Things Don't Match The With Statements You set up a series of match statements in a `with` construct as a way of avoiding a bunch of nested if statements. Inevitably you will be passing data through that doesn't meet all of the match criteria. By default, the `with` construct will short circuit and your program will continue from there. You can, however, take more control over how you handle the _failure_ cases by employing an `else` block. The `else` block works a lot like a case statement. ```elixir with %{status_code: 200, body: body} <- HTTPoison.get!(url), {:ok, decoded_body} <- Poison.decode(body) do {:ok, decoded_body} else %{status_code: 401} -> reauthenticate() _ -> log_error() end ``` Here we are able to anticipate a _failure_ case and respond accordingly. For everything else, we have a generic action that we take. See the [docs for `with`](https://hexdocs.pm/elixir/Kernel.SpecialForms.html#with/1) for more details. ================================================ FILE: elixir/word-lists-for-atoms.md ================================================ # Word Lists For Atoms The `~w` sigil works similarly to Ruby's `%w` (word array notation). It allows you to create a list of words (i.e. strings). ```elixir > ~w(one two three) ["one", "two", "three"] ``` It sets itself apart though with some modifiers. The default behavior matches the `s` modifier (for strings). ```elixir > ~w(one two three)s ["one", "two", "three"] ``` Where it gets more interesting is with the `a` modifier allowing you to create a list of atoms. ```elixir > ~w(one two three)a [:one, :two, :three] ``` Note: there is a third modifier, `c`, for char lists. ```elixir > ~w(one two three)c ['one', 'two', 'three'] ``` [source](http://elixir-lang.org/getting-started/sigils.html) ================================================ FILE: gatsby/add-javascript-to-body-of-the-document.md ================================================ # Add JavaScript To Body Of The Document Sometimes your JavaScript script tag needs to be placed in the body of the document. Like inside the `` tag toward the bottom. With Gatsby you don't have direct access to the outer HTML document without copying it out of the cache. And you don't need to copy it out of the cache. Gatsby offers an SSR (server-side rendering) API for rendering content into various parts of the document. Here is how we can use the [`onRenderBody`](https://www.gatsbyjs.org/docs/ssr-apis/#onRenderBody) callback to add a `, ]) } ``` `onRenderBody` provides several functions including `setPostBodyComponents`. This takes an array of React fragments that will be injected at the bottom of the document body. [source](https://github.com/gaearon/overreacted.io/pull/55/files) ================================================ FILE: git/accessing-a-lost-commit.md ================================================ # Accessing A Lost Commit If you have lost track of a recent commit (perhaps you did a reset), you can generally still get it back. Run `git reflog` and look through the output to see if you can find that commit. Note the sha value associated with that commit. Let's say it is `39e85b2`. You can peruse the details of that commit with `git show 39e85b2`. From there, the utility belt that is git is at your disposal. For example, you can `cherry-pick` the commit or do a `rebase`. ================================================ FILE: git/add-a-range-of-filenames-to-gitignore.md ================================================ # Add A Range Of Filenames To gitignore The `.gitignore` file is a file where you can list files that should be ignored by git. This will prevent them from showing up in diffs, `git status`, etc. Most entries in the `.gitignore` file will plainly correspond to a single file. ``` # ignore env var files .env .env.local ``` Sometimes a project has a bunch of similarly named files. Autogenerated files are a prime example. For instance, a web app project may contain several sitemap files with incrementing suffix values (i.e. `sitemap-1.xml`, `sitemap-2.xml`, `sitemap-3.xml`, ...). I'd like to avoid having to type those all out in my `.gitignore` file. And I don't want to have to add new entries whenever another increment of the file is generated. I can handle all the current ones and future ones in a single line using some range pattern matching supported by the `.gitignore` file format. ``` # ignore sitemap files public/sitemap-[1-99].xml ``` This will ignore any sitemap files suffixed with 1 to 99. I don't really expect there to ever be more than handful of those files, so _99_ should definitely do the trick. [source](https://www.golinuxcloud.com/gitignore-examples/#5_Examples_of_pattern_matching_in_gitignore) ================================================ FILE: git/add-only-tracked-files-from-a-directory.md ================================================ # Add Only Tracked Files From A Directory The two extremes of staging files in a git repo are to either selectively pick each individual chunk of changes with `git add --patch` (my preference!) or to run `git add -A` to add everything. Now let's say I have large directory full of files that get generated during test runs. Most of these files are tracked (already checked in to the repository). There are also many new files generated as part of the most recent test run. I want to stage the changes to files that are already tracked, but hold off on doing anything with the new files. Running `git add spec/cassettes` won't do the trick because that will pull in everything. Running `git add --patch spec/cassettes` will take long and be tedious. Instead what I want is the `-u` flag. It's short for _update_ which means it will only stage already tracked files. ```bash $ git add -u spec/cassettes ``` That will stage every change to any already known files in `spec/cassettes`. See `man git-add` for more details. ================================================ FILE: git/amend-author-of-previous-commit.md ================================================ # Amend Author Of Previous Commit The author of the previous commit can be amended with the following command ```bash $ git commit --amend --author "Don Draper " ``` [source](http://stackoverflow.com/questions/750172/change-the-author-of-a-commit-in-git) ================================================ FILE: git/auto-squash-those-fixup-commits.md ================================================ # Auto-Squash Those Fixup Commits As a fan of [atomic commits](https://dev.to/jbranchaud/atomic-commits-4hk2), I sometimes find myself with some changes that are ready to stage that belong on an earlier commit. If it is the immediately previous, I'll do an `--amend`. For anything further back than that, I've come to love the use of `git commit --fixup `. Once one or more _fixup_ commits are on the current branch, they'll need to be _squashed_ into the commits for which they've been targeted. This calls for a `git rebase`. Git knows how to squash fixup commits into the correct place, but you have to tell it to do so automatically. You can do that one of two ways. You can either include the `--autosquash` flag each time you rebase: ```bash $ git rebase -i --autosquash ``` Or you can tell Git to always autosquash in your `~/.gitconfig`: ``` [rebase] autoSquash = true ``` See the section on `--autosquash` in `man git-rebase` for more details. ================================================ FILE: git/better-diffs-with-delta.md ================================================ # Better Diffs With Delta A `git diff` from the command line is relatively bare bones. It shows you removed lines and added lines that make up a changeset with the former text in red and the later text in green. All other contextual text is in white. I've found this to be good enough for most of the life of my git usage. I've been missing out though. By using [`delta`](https://github.com/dandavison/delta) as the pager and diff filter for `git`, I get a bunch of nice visual improvements. - Removals and additions are red and green shaded backgrounds - Syntax highlighting for most languages - Highlight specific part of a line that has changed - Visual spacing and layout is clearer To get all of this, all I had to do was install `delta`: ```bash $ brew install delta ``` And then add `delta` as both the _core_ pager and `diffFilter` in my global git config file: ``` [core] pager = delta [interactive] singleKey = true # unrelated, but nice to have diffFilter = delta --color-only ``` It's also recommended that you use `zdiff3` for your merge conflict style, which I already had: ``` [merge] conflictstyle = zdiff3 ``` Once you have ths all configred, try a `git diff` or `git add --patch` and see how much more visual info you get. ================================================ FILE: git/caching-credentials.md ================================================ # Caching Credentials When public key authentication isn't an option, you may find yourself typing your password over and over when pushing to and pulling from a remote git repository. This can get tedious. You can get around it by configuring git to cache your credentials. Add the following lines to the `.git/config` file of the particular project. ``` [credential] helper = cache --timeout=300 ``` This will tell git to cache your credentials for 5 minutes. Use a much larger number of seconds (e.g. 604800) to cache for longer. Alternatively, you can execute the command from the command line like so: ```bash $ git config credential.helper 'cache --timeout=300' ``` [source](http://git-scm.com/docs/git-credential-cache) ================================================ FILE: git/change-the-start-point-of-a-branch.md ================================================ # Change The Start Point Of A Branch More than a few times I have checked out a new branch against, say, `develop` when I instead meant to base it off `qa`. I've tried what felt like the obvious solution. ```bash ❯ git checkout qa ❯ git checkout -b new-branch fatal: A branch named 'new-branch' already exists. ``` Git won't allow this. The fix I tend to go with is to delete the branch, move to my intended starting point, and check it out anew. Here is another approach. The `git checkout` command offers the `-B` flag which will save me a step. ```bash ❯ git checkout -B new-branch Switched to and reset branch 'new-branch' ``` Use this with caution. Any commits that have been applied to the subject branch will be reset (read: wiped out) in the process. See `man git-checkout` for more details. ================================================ FILE: git/check-how-a-file-is-being-ignored.md ================================================ # Check How A File Is Being Ignored There are a few places on your machine where you can specify the files that git should ignore. The most common is a repository's `.gitignore` file. The other places those excludes are specified can be more obscure. Fortunately, `git check-ignore` is a command that can show you specifically where. For instance, let's check why my `notes.md` file is being ignored. ```bash $ git check-ignore -v .DS_Store .git/info/exclude:7:notes.md notes.md ``` At some point I added it to my repo's `.git/info/exclude` file. The `-v` flag (_verbose_) when included with `check-ignore` tells me the file location. How about these pesky `.DS_Store` directories? How are those being ignored? ```bash $ git check-ignore -v .DS_Store /Users/jbranchaud/.gitignore:3:.DS_Store .DS_Store ``` Ah yes, I had added it to my _global exclude file_ which I've configured in `~/.gitconfig` to be the `~/.gitignore` file. See `man git-check-ignore` for more details. ================================================ FILE: git/check-if-a-file-has-changed-in-a-script.md ================================================ # Check If A File Has Changed In A Script If I'm at the command line and I want to check if a file has changed, I can run `git diff` and see what has changed. If I want to be more specific, I can run `git diff README.md` to see if there are changes to that specific file. If I'm trying to do this check in a script though, I want the command to clearly tell the script _Yes_ or _No_. Usually a script looks for an exit code to determine what path to take. But as long as `git diff` runs successfully, regardless of whether or not their are changes, it is going to have an affirmative exit code of `0`. This is why `git diff` offers the `--exit-code` flag. > Make the program exit with codes similar to diff(1). That is, it exits with 1 > if there were differences and 0 means no differences. With that in mind, we can wire up a script with `git diff` that takes different paths depending on whether or not there are changes. ```bash if ! git diff --exit-code README.md; then echo "README.md has changes" else echo "README.md is clean" fi ``` We can take this a step further and instead use the `--quiet` flag. > Disable all output of the program. Implies --exit-code. Disables execution of > external diff helpers whose exit code is not trusted This exhibits the same behavior as `--exit-code` and goes the additional step of silencing diff output and disabling execution of external diff helpers like `delta`. See `man git-diff` for more details. ================================================ FILE: git/check-if-a-file-is-under-version-control.md ================================================ # Check If A File Is Under Version Control The `git ls-files` command can be used with the `--error-unmatch` flag to check if a file is under version control. It does this by checking if any of the listed files appears on the _index_. If any does not, it is treated as an error. In a project, I have a `README.md` that is under version control. And I have `node_modules` that shouldn't be under version control (which is why they are listed in my `.gitignore` file). I can check the README and a file somewhere in `node_modules`. ```bash ❯ git ls-files --error-unmatch README.md README.md ❯ git ls-files --error-unmatch node_modules/@ai-sdk/anthropic/CHANGELOG.md error: pathspec 'node_modules/@ai-sdk/anthropic/CHANGELOG.md' did not match any file(s) known to git Did you forget to 'git add'? ``` Notice the second command results in an error because of the untracked `CHANGELOG.md` file in `node_modules`. Here is another example of this at work while specifying multiple files: ```bash ❯ git ls-files --error-unmatch README.md node_modules/@ai-sdk/anthropic/CHANGELOG.md package.json README.md package.json error: pathspec 'node_modules/@ai-sdk/anthropic/CHANGELOG.md' did not match any file(s) known to git Did you forget to 'git add'? ``` Each tracked file gets listed and then the untracked file results in an error. See `man git-ls-files` for more details. ================================================ FILE: git/checking-commit-ancestry.md ================================================ # Checking Commit Ancestry I have two commit shas and I want to know if the first is an ancestor of the second. Put another way, is this first commit somewhere in the history of this other commit. Git's `merge-base` command combined with the `--is-ancestor` flag makes answering this question easy. Furthermore, because it is a plumbing command, it can be used in a script or sequence of commands as a switch based on the answer. Here is an example of this command in action: ```bash $ git merge-base --is-ancestor head~ head && echo 'yes, it is' yes, it is $ git merge-base --is-ancestor head~ head~~ && echo 'yes, it is' ``` In the first command, `head~` is clearly an ancestor of `head`, so the `echo` command is triggered. In the second, `head~` is not an ancestor of `head~~` so the return status of 1 short-circuits the rest of the command. Hence, no `echo`. See `man git-merge-base` for more details. [source](http://stackoverflow.com/questions/18345157/how-can-i-tell-if-one-commit-is-an-ancestor-of-another-commit-or-vice-versa) ================================================ FILE: git/checkout-old-version-of-a-file.md ================================================ # Checkout Old Version Of A File When you want to return to a past version of a file, you can reset to a past commit. When you don't want to abandon a bunch of other changes, this isn't going to cut it. Another option is to just checkout the particular file as it was at the time of a past commit. If the sha of that past commit is `72f2675` and the file's name is `some_file.rb`, then just use checkout like so: ``` $ git checkout 72f2675 some_file.rb ``` ================================================ FILE: git/checkout-previous-branch.md ================================================ # Checkout Previous Branch Git makes it easy to checkout the last branch you were on. ```bash $ git checkout - ``` This is shorthand for `git checkout @{-1}` which is a way of referring to the previous (or last) branch you were on. You can use this trick to easily bounce back and forth between `master` and a feature branch. [source](http://stackoverflow.com/questions/7206801/is-there-any-way-to-git-checkout-previous-branch) ================================================ FILE: git/cherry-pick-a-range-of-commits.md ================================================ # Cherry Pick A Range Of Commits Git's `cherry-pick` command allows you to specify a range of commits to be cherry picked onto the current branch. This can be done with the `A..B` style syntax -- where `A` is the older end of the range. Consider a scenario with the following chain of commits: `A - B - C - D`. ```bash $ git cherry-pick B..D ``` This will cherry pick commits `C` and `D` onto `HEAD`. This is because the lower-bound is exclusive. If you'd like to include `B` as well. Try the following: ```bash $ git cherry-pick B^..D ``` See `man git-cherry-pick` for more details. ================================================ FILE: git/cherry-pick-multiple-commits-at-once.md ================================================ # Cherry Pick Multiple Commits At Once I've always thought of `git cherry-pick` as being a command that you can run against a single commit by specifying the SHA of that commit. That's how I've always used it. The man page for `git-cherry-pick` plainly states: > Given one or more existing commits, apply the change each one introduces, > recording a new commit for each. We can cherry pick multiple commits at once in a single command. They will be applied one at a time in the order listed. Here we can see an example of applying two commits to the current branch and the accompanying output as they are auto-merged. ```bash $ git cherry-pick 5206af5 6362f41 Auto-merging test/services/event_test.rb [jb/my-feature-branch 961f3deb] Use the other testing syntax Date: Fri May 2 10:50:14 2025 -0500 1 file changed, 7 insertions(+), 7 deletions(-) Auto-merging test/services/event_test.rb [jb/my-feature-branch b15835d0] Make other changes to the test Date: Fri May 2 10:54:48 2025 -0500 1 file changed, 7 insertions(+), 7 deletions(-) ``` If the commits cannot be cleanly merged, then you may need to do some manual resolution as they are applied. Or maybe you want to try including the `-Xpatience` merge strategy. See `man git-cherry-pick` for more details. Make sure to look at the _Examples_ section which contains much more advanced examples beyond what is shown above. ================================================ FILE: git/clean-out-all-local-branches.md ================================================ # Clean Out All Local Branches Sometimes a project can get to a point where there are so many local branches that deleting them one by one is too tedious. This one-liner can help: ``` $ git branch --merged master | grep -v master | xargs git branch -d ``` This won't delete branches that are unmerged which saves you from doing something stupid, but can be annoying if you know what you are doing. If you are sure you want to wipe everything, just use `-D` like so: ``` $ git branch --merged master | grep -v master | xargs git branch -D ``` [source](https://twitter.com/steveklabnik/status/583055065868447744) ================================================ FILE: git/clean-out-working-copy-with-patched-restore.md ================================================ # Clean Out Working Copy With Patched Restore I sometimes let the working copy of my projects get a little messy. The working copy is all the changes I've made to tracked files that haven't been staged or commited. After working for a bit, especially on something more exploratory, I end up with comments, log statements, and debugging calls scattered across a bunch of files. If these exploratory changes are mixed in with a bunch of actual changes, it can create a lot noise. I can clean up that noise by restoring the files. I can be surgical about it with the `--patch` flag. ```bash $ git restore --patch ``` This will prompt me for each changeset. - `y` -- yes, restore that change - `n` -- no, leave it there - `q` -- bail out of the restore There are other _patch_ options, but these are the ones I use the most. To see what the rest of the options are, go to `man git-add` and find `patch` in the `INTERACTIVE MODE` section. ================================================ FILE: git/clean-up-old-remote-tracking-references.md ================================================ # Clean Up Old Remote Tracking References After working on a Git-versioned project for a while, you may find that there are a bunch of references to remote branches in your local repository. You know those branches definitely don't exist on the remote server and you've removed the local branches, but you still have references to them lying around. You can reconcile this discrepancy with one command: ```bash $ git fetch origin --prune ``` This will prune all those non-existent remote tracking references which is sure to clean up your git log (`git log --graph`). [source](http://stackoverflow.com/a/3184742/535590) ================================================ FILE: git/clear-entries-from-git-stash.md ================================================ # Clear Entries From Git Stash I often stash changes as I'm moving between branches, rebasing, or pulling in changes from the remote. Usually these are changes that I will want to restore with a `git stash pop` in a few moments. However, sometimes these stashed changes are abandoned to time. When I run `git stash list` on an active project, I see that there are nine entries in the list. When I do `git show stash@{0}` and `git show stash@{1}` to see the changes that comprise the latest two entries, I don't see anything I care about. I can get rid of those individual entries with, say, `git stash drop stash@{0}`. But I'm pretty confident that I don't care about any of the nine entries in my stash list, so I want to _clear_ out all of them. I can do that with: ```bash $ git stash clear ``` Now when I run `git stash list`, I see nothing. See `man git-stash` for more details. ================================================ FILE: git/clone-a-repo-just-for-the-files-without-history.md ================================================ # Clone A Repo Just For The Files, Without History Though the history of a Git repository is a huge part of its value, sometimes you just want a copy of the files for the current state of the main branch. Using the `--depth` flag with `git-clone` allows you to clone a repo without its history. You can do that with a depth of `1` which will clone the top of the tree and exclude all the past history. ```bash $ git clone --depth 1 git@github.com:jbranchaud/til.git ``` If you do a `git log` after this, you'll see there is only one commit in the history. Depending on the size and history of the repo, you may notice that the clone is quicker than one that includes the full history. See `man git-clone` for more details. ================================================ FILE: git/clone-a-repo-locally-from-git.md ================================================ # Clone A Repo Locally From .git If you want to get a clean copy of a repository that you have locally, there is no need to go over the wire cloning it from the remote. You can clone from a local copy. ```bash $ git clone my-repo/.git path/to/fresh-copy ``` See `man git-clone` for more details. ================================================ FILE: git/configure-global-gitignore-file.md ================================================ # Configure Global gitignore File There are some files that you know you'll never want tracked by git. You don't even want them showing up as an option. For these files, it can be nice to specify them in a global `.gitignore` file. Then you don't need to specify them for each project. This can be configured in `~/.gitconfig` under the `core` settings as `excludesFile`. ``` [core] excludesFile = ~/.gitignore ``` Then, create `~/.gitignore` (that's as good a place as any to put it). ``` # global .gitignore .DS_Store wip_notes.md ``` You'll now notice that for any git project, the files you listed won't be showing up in the untracked list. You can also add this to your `~/.gitconfig` with this one-line command. ```bash $ git config --global core.excludesfile ~/.gitignore ``` See `man git-config` and search `core.excludesFile` for more details. ================================================ FILE: git/configuring-the-pager.md ================================================ # Configuring The Pager When you run Git commands that produce a bunch of output, Git will use a pager to present the content starting at the beginning, rather than spitting it all out to the screen at once. This will almost always come in handy for commands like `git-diff` and `git-log`. By default, Git uses `less` as its pager. You can also choose to configure it to something else by updating your git config file or by running the following command: ```bash $ git config --global core.pager 'more' ``` If you'd like to turn the pager off altogether, set it to a blank string. ```bash $ git config --global core.pager '' ``` [source](https://git-scm.com/book/en/v2/Customizing-Git-Git-Configuration) ================================================ FILE: git/copy-a-file-from-another-branch.md ================================================ # Copy A File From Another Branch After doing some work on a large feature branch, I wanted to split out some of the work into a separate smaller branch to create an initial PR. There were several files on the feature branch that I knew needed to be part of this smaller branch. To bootstrap this smaller branch, I need to check it out from main and then copy over the needed files. First, let's create a fresh branch: ```bash $ git branch --show-current large-feature-branch $ git checkout main $ git checkout -b new-smaller-branch ``` Now I can start pulling over the files I care about: ```bash $ git checkout large-feature-branch some/file/ICareAbout.js ``` This form of `git-checkout` looks on the specified branch for the specified file and copies that over to the index. Note: this will copy over any local, working tree changes that you've made to the named file. ================================================ FILE: git/count-all-files-of-specific-type-tracked-by-git.md ================================================ # Count All Files Of Specific Type Tracked By Git I want to get a count of all the markdown files in my [TIL repo](https://github.com/jbranchaud/til). Since all the files I care about are tracked by `git`, I can use `git ls-files` to get a listing of all files. That command on its own lists all files tracked by your git repository. Though there are many other flags we can apply, that will do for my purposes. By giving `git ls-files` a pattern to match against, I can turn up just, for instance, markdown files (`*.md`). I can pipe that to `wc -l` to get a count rather than exploding my terminal with a list of file names. ```bash ❯ git ls-files '*.md' | wc -l 1503 ``` That command includes `README.md` and `CONTRIBUTING.md`, but really I only want to count the markdown files that constitute a TIL. Those all happen to be nested under a single directory. So I can tweak the glob pattern like so: ```bash ❯ git ls-files '*/*.md' | wc -l 1501 ``` See `man git-ls-files` for more details. ================================================ FILE: git/count-number-of-commits-on-a-branch.md ================================================ # Count Number Of Commits On A Branch The `git rev-list` command will show all commits that fit the given revision criteria. By adding in the `--count` flag, we get a count of the number of commits that would have been displayed. Knowing this, we can get the count of commits for the current branch like so: ```bash $ git rev-list --count HEAD 4 ``` This finds and counts commits from `HEAD` (usually the top of the current branch) all the back in reverse chronological order to the beginning of the branch (typically the beginning of the repository). This works exactly as expected for a the `main` branch. What about when we are on a feature branch though? Let's say we've branched off `main` and made a few commits. And now we want the count. ```bash $ git rev-list --count HEAD 7 ``` Unfortunately, that is counting up the commits on the feature branch but it keeps counting all the way back to the beginning of the repo. If we want a count of just the commits on the current branch, then we can specify a range: from whatever `main` was when we branched to the `HEAD` of this branch. ```bash $ git rev-list --count HEAD 3 ``` This is the same as saying, I want all commits on `HEAD`, but exclude (`^`) the commits on `main`: ```bash git rev-list --count HEAD ^main 3 ``` See `man git-rev-list` for more details. ================================================ FILE: git/create-a-new-branch-with-git-switch.md ================================================ # Create A New Branch With Git Switch As of [Git 2.23](https://www.infoq.com/news/2019/08/git-2-23-switch-restore/), there is a new command in town for change and creating branches -- `git-switch`. As a git user, you may be used to using `git checkout -b my_branch` to create and switch to a new branch called `my_branch`. The `git-checkout` command can do that and a lot more. In order to reduce some confusion and create a more explicit command for this kind of action. That's what brought about `git-switch`. Create and change to a new branch with `git-switch` and the `-c` flag: ```bash $ git switch -c my_new_branch ``` The `-c` flag is short for `--create` and the docs describe it as "a convenient shortcut for:" ```bash $ git branch $ git switch ``` See `man git-switch` for more details. ================================================ FILE: git/delete-all-untracked-files.md ================================================ # Delete All Untracked Files Git provides a command explicitly intended for cleaning up (read: removing) untracked files from a local copy of a repository. > git-clean - Remove untracked files from the working tree Git does want you to be explicit though and requires you to use the `-f` flag to force it (unless otherwise configured). Git also gives you fine-grained control with this command by defaulting to only deleting untracked files in the current directory. If you want directories of untracked files to be removed as well, you'll need the `-d` flag. So if you have a local repository full of untracked files you'd like to get rid of, just: ```bash $ git clean -f -d ``` or just: ```bash $ git clean -fd ``` [source](http://stackoverflow.com/questions/61212/remove-local-untracked-files-from-my-current-git-branch) ================================================ FILE: git/determine-absolute-path-of-top-level-project-directory.md ================================================ # Determine Absolute Path Of Top-Level Project Directory The `git rev-parse` command is a git plumbing command for parsing different kinds of things in git into a canonical form that can be used in a deterministic way by scripts. I would typically think of using it to work with branch names, tags, and other kinds of refs. There is a handy, sorta off-label use for it in determining the absolute path of the root directory for the current git repository. Use the `--show-toplevel` flag with no other arguments. ```bash ❯ git rev-parse --show-toplevel /Users/lastword/dev/jbranchaud/til ``` Here, I am in the local copy of [my TIL repo](https://github.com/jbranchaud/til). This command gives me the absolute path of the top-level directory where that `.git` directory resides. This is useful for scripts that need to orient themselves to the current project's top-level directory regardless of what directory they are being executed from. This is useful for things like a git hook script or monorepos with scripts located in a specific sub-project directory. Also worth mentioning is the `--show-superproject-working-tree` flag. In my TIL repo, I have a private repository included as a submodule. Within that directory `--show-toplevel` will produce the absolute path to the submodule. If I instead want the absolute path of the _super project_ (in this case TIL), then I can use this other flag. ```bash ❯ git rev-parse --show-toplevel /Users/lastword/dev/jbranchaud/til/notes ❯ git rev-parse --show-superproject-working-tree /Users/lastword/dev/jbranchaud/til ``` See `man git-rev-parse` for more details. ================================================ FILE: git/determine-the-hash-id-for-a-blob.md ================================================ # Determine The Hash Id For A Blob Git's `hash-object` command can be used to determine what hash id will be used by git when creating a blob in its internal file system. ```bash $ echo 'Hello, world!' > hola $ git hash-object hola af5626b4a114abcb82d63db7c8082c3c4756e51b ``` When a commit happens, git will generate this digest (hash id) based on the contents of the file. The name and location of the file don't matter, just the contents. This is the magic of git. Anytime git needs to store a blob, it can quickly match against the hash id in order to avoid storing duplicate blobs. Try it on your own machine, you don't even need to initialize a git repository to use `git hash-object`. [source](http://ftp.newartisans.com/pub/git.from.bottom.up.pdf) ================================================ FILE: git/diffing-with-patience.md ================================================ # Diffing With Patience The default diff algorithm used by Git is pretty good, but it can get mislead by larger, complex changesets. The result is a noisier, misaligned diff output. If you'd like a diff that is generally a bit cleaner and can afford a little slow down (you probably can), you can instead use the `patience` algorithm which is described as such: > Patience Diff, instead, focuses its energy on the low-frequency > high-content lines which serve as markers or signatures of important > content in the text. It is still an LCS-based diff at its core, but with > an important difference, as it only considers the longest common > subsequence of the signature lines: > Find all lines which occur exactly once on both sides, then do longest > common subsequence on those lines, matching them up. You can set this as the default algorithm by adding the following lines to your `~/.gitconfig` file: ``` [diff] algorithm = patience ``` or it can be set from the command line with: ```bash $ git config --global diff.algorithm patience ``` [source](http://bryanpendleton.blogspot.com/2010/05/patience-diff.html) h/t Josh Davey ================================================ FILE: git/dropping-commits-with-git-rebase.md ================================================ # Dropping Commits With Git Rebase I've been warned enough times about the potential dangers of `git reset --hard ...` that I always second guess myself as I type it out. Is it `git reset --hard HEAD` or `git reset --hard HEAD~`? If the working directory and index are clean, then there is another way to remove commits. A way that gives me more confidence about what exactly is being removed. Doing an interactive rebase gives you a number of options. One of those options is `d` (which stands for `drop`). ``` $ git rebase -i master ``` This pulls up an interactive rebase with all commits going back to what is on master -- great for when working from a feature branch. ``` pick 71ed173 Add Create A Stream From An Array as a reasonml til pick 80ac8d3 Add some clarity by distinguishing var names d 4f06c32 Add Data Structures With Self-Referential Types as a reasonml til d 01a0e75 Fix the name of this file ``` Adding `d` next to the commits you want to get rid of and saving will drop those commits. The great part is that there is zero ambiguity about which ones are being dropped. h/t Jake Worth ================================================ FILE: git/dry-runs-in-git.md ================================================ # Dry Runs in Git There are a few commands in git that allow you to do a *dry run*. That is, git will tell you the effects of executing a command without actually executing that command. For instance, if you are clearing out untracked files, you can double check what files are going to be deleted with the *dry run* flag, like so: ``` $ git clean -fd --dry-run Would remove tmp.txt Would remove stuff/ ``` Similarly, if you want to check in which files a commit is going to be incorporated, you can: ``` $ git commit --dry-run --short M README.md A new_file.rb ``` Try running `git commit --dry-run` (that is, without the `--short` flag). Look familiar? That is the same output you are getting from `git status`. ================================================ FILE: git/exclude-a-directory-during-a-command.md ================================================ # Exclude A Directory During A Command Many of the git commands we use, such as `git add`, `git restore`, etc., target files and paths relative to the current directory. This is typically exactly what we want, to stage and unstage and so forth the files and directories in front of us. I recently ran into a situation where I needed to restore a small subset of changes. At the same time, I had a massive number of auto-generated files recording HTTP interactions (hundreds of files, modified on the working tree). I wanted to run a `git restore`, but wading through all those HTTP recording files was not feasible. I needed to exclude those files. They all belonged to a `spec/cassettes` directory. I could exclude them with a _pathspec_ magic signature pattern which is used to alter and limit the paths in a git command. A _pathspec_ magic signature is a special pattern made up of a `:` followed by some signature declaring what the pattern means. The `(exclude)`, `!`, and `^` magic signatures all mean the same thing — exclude. So, we can exclude a directory from a `git restore` command like so: ```bash $ git restore --patch -- . ':!spec/cassettes' ``` We've employed two pathspec patterns here. The first, `.`, scopes everything to the current directory. The second, `':!spec/cassettes'` excludes everything in the `spec/cassettes` directory. See `man gitglossary` for more on _pathspecs_. ================================================ FILE: git/exclude-a-file-from-a-diff-output.md ================================================ # Exclude A File From A Diff Output When running `git diff `, you'll see output for all files changed at that commit. It's the same if you do a range of commits (e.g. `git diff ...`). If you are looking for changes in a particular part of the tree, then other changes will be a distraction. Some generated files, such as `yarn.lock`, can create a lot of noise in the diff output. You can get a more focused output by excluding certain files and paths. The syntax for doing that, however, is a bit wonky. To exclude a file, you have to add an argument formatted like `':(exclude)`. For instance, to exclude `yarn.lock`: ```bash $ git diff -- . ':(exclude)yarn.lock' ``` or to exclude an entire directory: ```bash $ git diff -- . ':(exclude)spec/**' ``` [source](https://stackoverflow.com/questions/39931781/git-diff-stat-exclude-certain-files/39937070#39937070) ================================================ FILE: git/excluding-files-locally.md ================================================ # Excluding Files Locally Excluding (read: ignoring) files that should not be tracked is generally done by listing such files in a tracked `.gitignore` file. Though it doesn't make sense to list all kinds of excluded files here. For files that you'd like to exclude that are temporary or are specific to your local environment, there is another option. These files can be added to the `.git/info/exclude` file as a way of ignoring them locally. Add specific files or patterns as needed to that file and then save it. Relevant files will no longer show up as untracked files when you `git status`. h/t [Dillon Hafer](https://twitter.com/DillonHafer) ================================================ FILE: git/extend-git-with-custom-commands.md ================================================ # Extend Git With Custom Commands I recently learned about the [`git absorb`](https://github.com/tummychow/git-absorb) command. It is interesting in its own right, but I bring it up because it isn't a command that is built in to git. When I was looking at the installation instructions for it, it didn't say anything about how to _register_ the command with `git`. How is git supposed to know about it? How do you extend git with custom commands? What I learned exploring those questions is that `git` will execute any command on your _path_ with a `git-` naming convention. So, if I create a executable binary called `git-taco`, add it to my path, and then run `git taco` (notice, no dash when I run it), `git` will run my custom binary. In the same way, if you download `git-absorb` and add it to your path, `git` will run it for you when you enter `git absorb ...`. You can even type something like `where git-` and then hit tab to prompt your shell to display a list of a varity of other `git` commands, most of which probably ship with `git`. [source](https://twitter.com/jbrancha/status/1756361704160530555) ================================================ FILE: git/files-with-local-changes-cannot-be-removed.md ================================================ # Files With Local Changes Cannot Be Removed This is a nice quality-of-life feature in `git` that should help you avoid accidentally discarding changes that won't be retrievable. ```bash ❯ git rm .tool-versions error: the following file has local modifications: .tool-versions (use --cached to keep the file, or -f to force removal) ``` My `.tool-versions` file has some local changes. I don't realize that and I go to issue a `git rm` command on that file. Instead of quietly wiping out my changes, `git` lets me know I'm doing something destructive (these local changes won't be in the diff or the reflog). I can force the removal if I know what I'm doing with the `-f` flag. Or I can take the two step approach of calling `git restore` on that file and then `git rm`. The `--cached` flag is also interesting because it doesn't actually delete the file from my file system, but it does stage the file deletion with `git`. That means the file now shows up as one of my untracked files. See `man git-rm` for more details. ================================================ FILE: git/find-and-remove-files-that-match-a-name.md ================================================ # Find And Remove Files That Match A Name Let's say I have a bunch of `robots.txt` file scattered throughout my project. I want to find all instances of that file checked into git. I then want to remove that file from git. I can find all the instances of that file checked into git using the [`git-ls-files`](https://git-scm.com/docs/git-ls-files) command. ```bash $ git ls-files '**/robots.txt' project-a/public/robots.txt project-b/public/robots.txt apps/project-c/public/robots.txt ``` That results in a list of paths of those files regardless of how far down they are nested (because of the `**` glob pattern). And because `git-ls-files` is a _git plumbing_ command, it pipes cleanly into other unix commands. I can combine that first command with [`git rm`](https://git-scm.com/docs/git-rm) using the [`xargs`](https://man7.org/linux/man-pages/man1/xargs.1.html) command. ```bash $ git ls-files '**/robots.txt' | xargs git rm rm 'project-a/public/robots.txt' rm 'project-b/public/robots.txt' rm 'apps/project-c/public/robots.txt' ``` That takes each path from the first part of the command and passes it to `git rm` which stages it as a removed file. I can finalize my work by creating a commit from these staged changes. ================================================ FILE: git/find-the-date-that-a-file-was-added-to-the-repo.md ================================================ # Find The Date That A File Was Added To The Repo The `git log` command has a bunch of flags that you can use to filter commits and format their output. We can get `git log` to only show the date for a commit in the `short` format with the following flags: ```bash $ git log --pretty=format:"%ad" --date=short ``` We can also get `git log` to filter commits to just those that have files being added: ```bash $ git log --diff-filter=A ``` Like many `git` commands, we can restrict the output to those that match a path or file. ```bash $ git log -- README.md ``` If we put all of these together, then we have a one-line command for getting the date a specific file was added to the repository: ```bash $ git log --pretty=format:"%ad" --date=short --diff-filter=A -- README.md 2015-02-06 ``` See `man git-log` for more details. ================================================ FILE: git/find-the-initial-commit.md ================================================ # Find The Initial Commit By definition, the initial commit in a repository has no parents. You can exploit that fact and use `rev-list` to find the initial commit; a commit with no parents. ```bash $ git rev-list --max-parents=0 HEAD ``` The `rev-list` command lists all commits in reverse chronological order. By restricting them to those with at most 0 parents, you are only going to get root commits. Generally, a repository will only have a single root commit, but it is possible for there to be more than one. See `man git-rev-list` for more details. [source](http://stackoverflow.com/questions/5188914/how-to-show-first-commit-by-git-log) ================================================ FILE: git/fix-whitespace-errors-throughout-branch-commits.md ================================================ # Fix Whitespace Errors Throughout Branch Commits Let's say we've been working on some changes to our repository on a branch. We've made several commits. We are close to putting up a PR, but we want to make sure everything is tidied up. We run a check and see that there are some whitespace errors that should be fixed. ```bash $ git diff main --check README.md:1: trailing whitespace. +# git-playground script.sh:9: trailing whitespace. + ``` This post isn't able to show the highlighted whitespace errors, but we can see the warnings above. Rather than cluttering things with an additional commit that fixes these errors or manually cleaning up each commit, we can ask `git` to fix it for us. ```bash $ git rebase --whitespace=fix main ``` That will do a manual rebase of each commit addressing the whitespace errors. We can run the error check again and see no output, which means we are good to go. ```bash $ git diff main --check ``` See the section on `--whitespace` in `man git-apply` for more details. [source](https://git-scm.com/book/en/v2/Customizing-Git-Git-Configuration) ================================================ FILE: git/get-latest-commit-timestamp-for-a-file.md ================================================ # Get Latest Commit Timestamp For A File The `git log` command can tell you all the commits that touched a file. That can be narrowed down to the latest commit for that file with the `-1` flag. The commit that it reports can then be further formatted to with the `--format` flag. The `%ai` format pattern gives the date the commit was authored in an ISO 8601-like format. The `%aI` (capital `I`) gives the date the commit was authored strictly in the ISO 8601 format. Here are examples of both side by side: ```bash ❯ git log -1 --format=%ai -- README.md 2024-10-15 13:59:09 -0500 ❯ git log -1 --format=%aI -- README.md 2024-10-15T13:59:09-05:00 ``` I made use of this in a script where I needed to get an idea of when various files were most recently modified. See `man git-log` and the `PRETTY FORMATS` section for more details. ================================================ FILE: git/get-the-name-of-the-current-branch.md ================================================ # Get The Name Of The Current Branch There are a couple ways of doing this. If you are on Git 2.22+, then the `git-branch` porcelain command now supports a flag for this. ```bash $ git branch --show-current main ``` If you are on an older version of Git or looking for a plumbing command that is more appropriate for scripting, then `git-rev-parse` is what you're looking for. ```bash $ git rev-parse --abbrev-ref HEAD main ``` [source](https://stackoverflow.com/questions/6245570/how-to-get-the-current-branch-name-in-git) ================================================ FILE: git/get-the-short-version-of-the-latest-commit.md ================================================ # Get The Short Version Of The Latest Commit As part of some automated scripting for an app deployment I wanted to be able to get the short version of the latest commit to the current git repo. Git's `rev-parse` command is the perfect fit for this. ```bash $ git rev-parse --short HEAD 708248b ``` See `man git-rev-parse` for more details. [source](https://stackoverflow.com/questions/5694389/get-the-short-git-version-hash) ================================================ FILE: git/grab-a-single-file-from-a-stash.md ================================================ # Grab A Single File From A Stash In git, you can reference a commit SHA or branch to checkout differing versions of files. ```bash $ git checkout d3d2e38 -- README.md ``` In the same way, you can snag the version of a file as it existed in a stash. Just reference the relevant stash. ```bash $ git checkout stash@{1} -- README.md ``` [source](http://stackoverflow.com/questions/1105253/how-would-i-extract-a-single-file-or-changes-to-a-file-from-a-git-stash) ================================================ FILE: git/grep-for-a-pattern-on-another-branch.md ================================================ # Grep For A Pattern On Another Branch Git has a built-in `grep` command that works essentially the same as the standard `grep` command that unix users are used to. The benefit of `git-grep` is that it is tightly integrated with Git. You can search for occurrences of a pattern on another branch. For example, if you have a feature branch, `my-feature`, on which you'd like to search for occurrences of `user.last_name`, then your command would look like this: ```bash $ git grep 'user\.last_name' my-feature ``` If there are matching results, they follow this format: ``` my-feature:app/views/users/show.html.erb: <%= user.last_name %> ... ``` This formatting is handy because you can easily copy the branch and file directive for use with [`git-show`](viewing-a-file-on-another-branch.md). See `man git-grep` for more details. ================================================ FILE: git/grep-over-commit-messages.md ================================================ # Grep Over Commit Messages The `git log` command supports a `--grep` flag that allows you to do a text search (using grep, obviously) over the commit messages for that repository. For the git user that writes descriptive commit messages, this can come in quite handy. In particular, this can be put to use in an environment where the standard process involves including ticket and bug numbers in the commit message. For example, finding bug `#123` can be accomplished with: ```bash $ git log --grep="#123" ``` See `man git-log` for more details. ================================================ FILE: git/highlight-extra-whitespace-in-diff-output.md ================================================ # Highlight Extra Whitespace In Diff Output When running a `git diff` (or `git add --patch`) I'll sometimes come across lines that don't have any visible changes. This is usually because some whitespace characters were either added (on accident) or removed (often by a autoformatter). Depending on the `core.whitespace` config, you'll probably see at least some of the whitespace errors that git provides. By default, git only highlights whitespace errors on added (`new`) lines. However if some extra whitespace was originally committed and is now being removed, it won't be highlighted on the `old` line in the diff. We can have git always highlight whitespace errors by setting `wsErrorHighlight` to `all` in the global git config. ```bash $ git config --global diff.wsErrorHighlight all ``` Which updates the global gitconfig file with the following line: ``` [diff] wsErrorHighlight = all ``` The `all` option is a shorthand for `old,new,context`. See `man git-diff` for more details. ================================================ FILE: git/highlight-small-change-on-single-line.md ================================================ # Highlight Small Change On Single Line Sometimes a change gets made on a single, long line of text in a Git tracked file. If it is a small, subtle change, then it can be hard to pick out when looking at the diff. A standard diff will show a green line of text stacked on a red line of text with no more granular information. There are two ways we can improve the diff output in these situations. The first is built-in to git. It is the `--word-diff` flag which will visually isolate the portions of the line that have changed. ```bash git diff --word-diff README.md ``` Which will produce something like this: ```diff A collection of concise write-ups on small things I learn [-day to day-]{+day-to-day+} across a ``` The outgoing part is wrapped in `[-...-]` and the incoming part is wrapped in `{+...+}`. The second (and my preference) is to use [`delta`](https://github.com/dandavison/delta) as an external differ and pager for git. ```bash git -c core.pager=delta diff README.md ``` I cannot visually demonstrate the difference in a standard code block. So I'll describe it. We see a red and green line stacked on each other, but with muted background colors. Then the specific characters that are different stand out because they are highlighted with brighter red and green. I [posted a visual here](https://bsky.app/profile/jbranchaud.bsky.social/post/3ln245orlxs2j). `delta` can also be added as a standard part of your config like I demonstrate in [Better Diffs With Delta](git/better-diffs-with-delta.md). h/t to [Dillon Hafer's post on `--word-diff`](https://til.hashrocket.com/posts/t994rwt3fg-finds-diffs-in-long-line) ================================================ FILE: git/ignore-changes-to-a-tracked-file.md ================================================ # Ignore Changes To A Tracked File Files that should never be tracked are listed in your `.gitignore` file. But what about if you want to ignore some local changes to a tracked file? You can tell git to assume the file is unchanged ```bash $ git update-index --assume-unchanged ``` Reversing the process can be done with the `--no-assume-unchanged` flag. [source](http://blog.pagebakers.nl/2009/01/29/git-ignoring-changes-in-tracked-files/) ================================================ FILE: git/ignore-files-specific-to-your-workflow.md ================================================ # Ignore Files Specific To Your Workflow _If you want to watch instead of read, I explore this in [Four Ways to Ignore Files with Git](https://www.youtube.com/watch?v=ip06v7Wnfz0)._ The most common way to tell git to ignore files is to add them to a project's `.gitignore` file. This file is kept under version control, so it is shared with anyone who clones the project. What about ignoring files that shouldn't necessarily be recorded in the project's `.gitignore`? For instance, let's say I create a `notes.md` file to write some project notes to myself or keep track of a few todo items. This file is just for me. I don't want it committed. Because this `notes.md` is an idiosyncrasy of my workflow, I don't want to exclude it in the tracked `.gitignore` file. Instead, this file is a perfect candidate for the git repository's `.git/info/exclude` file. Git treats entries in this file the same as it does the `.gitignore` file. This file only exists on my machine and is not under version control. ``` # .git/info/exclude notes.md ``` Once I've added that line, `notes.md` will no longer show up as an untracked file when I run `git status`. See `man gitignore` for more details. ================================================ FILE: git/include-a-message-with-your-stashed-changes.md ================================================ # Include A Message With Your Stashed Changes If you were to quickly stash some changes, you end up with a stash reference that git would attach a little context to, such as the latest commit SHA and the first line of its commit message. ```bash $ git stash list stash@{0}: WIP on master: 6766419 Add link to source for latest TIL ``` Often this won't provide you the needed context to return to your stash and pick up where you left off even days later. You can customize the message of a stash with the `-m` flag. ```bash $ git stash push -m 'made some changes' ``` When you view your stash list, you'll see your custom message. ```bash $ git stash list stash@{0}: On master: made some changes ``` And hopefully that's the context you need to hit the ground running. See `man git-stash` for more details. ================================================ FILE: git/include-or-exclude-remaining-patch-changes.md ================================================ # Include Or Exclude Remaining Patch Changes I often use `git add --patch` as a way of interactively staging changes for a commit. Git walks me through each individual chunk of changes—which it calls _hunks_—so that I can include it or exclude it. Sometimes, like when I'm working with a `yarn.lock` file, there are a ton of computer-generated changes to a file that I don't want to individually confirm. One of the options while interactively staging is to hit `a` which will stage the current hunk and all later hunks for the current file. A complementary scenario arises at times when working with a Rails `schema.rb` file. There are a bunch of Postgres client-specific changes that I don't want to commit. Once I get to the `schema.rb` file, I can hit `d` to _not_ stage this or all remaining hunks in the current file. ``` a - stage this hunk and all later hunks in the file d - do not stage this hunk or any of the later hunks in the file ``` This saves me from hitting `y` a dozen times to accept changes or hitting `n` a dozen times to decline changes. See `man git-add` and look for the _Interactive mode_ section for more details. ================================================ FILE: git/include-some-stats-in-your-git-log.md ================================================ # Include Some Stats In Your Git Log A simple `git log` command is going to give you a concise set of information for each commit. Usually it is enough info. When it's not, `git log` can provide additional information with the right flags. To include overall and per-file stats on the number of insertions and deletions, use the `--stat` flag. ```bash $ git log --stat commit 66e67741a1cd6857a4467d1453c9f17ef5849f20 Author: jbranchaud Date: Mon Nov 13 21:24:41 2017 -0600 Add Focus The URL Bar as an internet til README.md | 3 ++- internet/focus-the-url-bar.md | 10 ++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) commit 9241e3919ef1e4f68b71a1491d368ae6361084aa Author: jbranchaud Date: Sat Nov 11 11:41:40 2017 -0600 Add Freeze An Object, Sorta as a javascript til README.md | 3 ++- javascript/freeze-an-object-sorta.md | 44 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+), 1 deletion(-) ... ``` See `man git-log` for more details. ================================================ FILE: git/intent-to-add.md ================================================ # Intent To Add Git commands like `git diff` and `git add --patch` are awesome, but their little caveat is that they only work on files that are currently tracked in the repository. That means that after working on that new feature for 30 minutes, a `git diff` is only going to show you the changes to existing files and when you go to start making commits with `git add --patch` you will again be provided with only part of the picture. The `git add` command has a flag, `-N`, that is described in the git man pages: > Record only the fact that the path will be added later. An entry for the > path is placed in the index with no content. This is useful for, among other > things, showing the unstaged content of such files > with git diff and committing them with git commit -a. By adding untracked files with the `-N` flag, you are stating your *intent to add* these files as tracked files. Once git knows that these files are meant to be tracked, it will know to include them when doing things like computing the diff, performing an interactive add, etc. ================================================ FILE: git/interactively-checkout-specific-files-from-a-stash.md ================================================ # Interactively Checkout Specific Files From A Stash This command will prompt you with a list of the stashes in your current git repo. Once you select one of them from the interactive fzf prompt, you will then be prompted with another fzf prompt, this second one allow multi-select. From there you can tab select the files you want to checkout. Once you've marked the ones you want, hit enter and those files will be checked out to your index. ```bash git stash show --name-only $( git stash list \ | fzf --height=20% --reverse \ | sed 's/:.*//' ) \ | fzf --height=20% --multi --sync \ | xargs -I {} sh -c 'git checkout stash@{0} -- {}' ``` ================================================ FILE: git/interactively-unstage-changes.md ================================================ # Interactively Unstage Changes I often use `git add --patch` to interactively stage changes for a commit. Git takes me through changes to tracked files piece by piece to check if I want to stage them. This same interactive _staging_ of files can be used in reverse when removing changes from the index. Just add the `--patch` flag. You can use it for a single file ```bash $ git reset --patch README.md ``` or you can let it operate on the entire repository ```bash $ git reset --patch ``` This is useful when you've staged part of a file for a commit and then realize that some of those changes shouldn't be committed. ================================================ FILE: git/keep-file-locally-with-git-rm.md ================================================ # Keep File Locally With `git rm` Let's say I've added a new file `data.json` to my repo as part of the most recent commit. I realize this isn't the point at which I want to add that file. So, I do `git rm data.json` and then `git commit --amend` to rework that commit. However, when I look in my working tree, or even just my file system, I'll notice that `data.json` is gone. The `git rm` command completely removed the file since it was previously an untracked file. To keep `git rm` from tossing out my file like that, I can include the `--cached` flag which will remove the file from the index (stages it to be `deleted`), but restore it to the working directory. ```bash $ git rm --cached data.json ``` See `man git-rm` for more details on the `--cached` flag. ================================================ FILE: git/last-commit-a-file-appeared-in.md ================================================ # Last Commit A File Appeared In In my project, I have a `README.md` file that I haven't modified in a while. I'd like to take a look at the last commit that modified it. The `git log` command can be used here with few arguments to narrow it down. ``` $ git log -1 -- README.md commit 6da76838549a43aa578604f8d0eee7f6dbf44168 Author: jbranchaud Date: Sun May 17 12:08:02 2015 -0500 Add some documentation on configuring basic auth. ``` This same command will even work for files that have been deleted if you know the path and name of the file in question. For instance, I used to have an `ideas.md` file and I'd like to find the commit that removed it. ``` $ git log -1 -- docs/ideas.md commit 0bb1d80ea8123dd12c305394e61ae27bdb706434 Author: jbranchaud Date: Sat May 16 14:53:57 2015 -0500 Remove the ideas doc, it isn't needed anymore. ``` ================================================ FILE: git/list-all-files-added-during-span-of-time.md ================================================ # List All Files Added During Span Of Time I wanted to get an idea of all the TIL posts I wrote during 2024. Every TIL I write is under version control in a [git repo on github](https://github.com/jbranchaud/til). That means git has all the info I need to figure that out. The `git diff` command is a good way at this problem. With the `--diff-filter=A` flag I can restrict the results to just files that were _Added_. And with `--name-only` I can cut all the other diff details out and get just filenames. But filenames added to which commits? We need to specify a ref range. There is a ton of flexibility in how you define a ref, including [a date specification suffix](https://git-scm.com/docs/gitrevisions#Documentation/gitrevisions.txt-emltrefnamegtltdategtemegemmasteryesterdayememHEAD5minutesagoem) that points to the value of the ref at an earlier point in time. So, how about from the beginning of 2024 to the beginning of 2025: ``` HEAD@{2024-01-01}..HEAD@{2025-01-01} ``` Putting that all together, we this command and potentially a big list of files. ```bash $ git diff --diff-filter=A --name-only HEAD@{2024-01-01}..HEAD@{2025-01-01} ``` I wanted to restrict the results to just markdown files, so I added a filename pattern. ```bash $ git diff --diff-filter=A --name-only HEAD@{2024-01-01}..HEAD@{2025-01-01} -- "*.md" ``` I could even go a step further to see only the files added to a specific directory. ```bash $ git diff --diff-filter=A --name-only HEAD@{2024-01-01}..HEAD@{2025-01-01} -- "postgres/*.md" ``` As a final bonus, I can spit out the github URLs for all those files with a bit of `awk`. ```bash $ git diff --diff-filter=A --name-only HEAD@{2024-01-01}..HEAD@{2025-01-01} -- "postgres/*.md" | awk '{print "https://github.com/jbranchaud/til/blob/master/" $0}' ``` ================================================ FILE: git/list-all-files-changed-between-two-branches.md ================================================ # List All Files Changed Between Two Branches The `git-diff` command can help with finding all files that have changed between two branches. For instance, if you are at the `HEAD` of your current feature branch and you'd like to see the list of files that have changed since being in sync with the `master` branch, you'd formulate a command like the following: ```bash $ git diff --name-only master ``` The `--name-only` flag is important because it cuts out the rest of the non-essential output. You'll want to list the _older_ branch first and the current branch second as a way of showing what has changed from the original branch to get to the current branch. Though the example shows the use of _branches_ any git reference can be used to see what has changed between two commits. See `man git-diff` for more details. ================================================ FILE: git/list-all-git-aliases-from-gitconfig.md ================================================ # List All Git Aliases From gitconfig Running the `git config --list` command will show all of the configuration settings you have for `git` relative to your current location. Though most of these setting probably live in `~/.gitconfig`, you may also have some locally specified ones in `.git/config`. This will grab them all including any `alias` entries. We can narrow things down to just `alias` entries using the `--get-regexp` flag. ```bash $ git config --get-regexp '^alias\.' alias.ap add --patch alias.authors shortlog -s -n -e alias.co checkout alias.st status alias.put push origin HEAD alias.fixup commit --fixup alias.squash commit --squash alias.doff reset HEAD^ alias.add-untracked !git status --porcelain | awk '/\?\?/{ print $2 }' | xargs git add alias.reset-authors commit --amend --reset-author -CHEAD ``` I use `git doff` all the time on feature branches to "pop" the latest commmit onto the working copy. I was trying to remember exactly what the `git doff` command is and this was an easy way to check. ================================================ FILE: git/list-branches-that-contain-a-commit.md ================================================ # List Branches That Contain A Commit You know a certain change made it onto the main branch. You'd like to know if that changes has been integrated back into the development and staging branches. If you have a specific git sha associated with that change, there is a handy way to check. ```bash $ git branch --contains 50e1151 ``` The `--contains` flag of the [`git-branch`](https://git-scm.com/docs/git-branch) command will list all branches locally that contain the given commit sha. If you don't see any output, then no branches have that sha. This means either there are remote changes that you need to pull down or you're looking at the wrong repo. See `man git-branch` for more details. ================================================ FILE: git/list-commits-on-a-branch.md ================================================ # List Commits On A Branch You can list all commits that have taken place since a branch split off from another branch using `git log`. You just need to specify a ref range with the originating branch and the branch of interest. ```bash $ git log master..my-feature-branch ``` That will list all commits on `my-feature-branch` since it branched off from `master`. I like to tighten up the output a bit with a flag: ```bash $ git log master..my-feature-branch --oneline ``` See `man git-log` for more details. ================================================ FILE: git/list-different-commits-between-two-branches.md ================================================ # List Different Commits Between Two Branches There are times when I want to get a sense of the difference between two branches. I don't want to look at the actual diff though, I just want to see what commits are on one and the other. I can do just this by using the `git-log` command with a couple flags, most importantly the `--cherry-pick` flag. To compare the `feature` branch against the `master` branch, I can run a command like the following: ```bash $ git log --left-right --graph --cherry-pick --oneline feature...branch ``` This lists commits with the first line of their messages. It also includes either a `<` or `>` arrow at the front of each commit indicating whether the commit is on the left (`feature`) or right (`master`) branch, respectively. Note: you can use more than branches in a command like this. Any two references will work. You can just use two SHAs for instance. [source](http://stackoverflow.com/questions/7566416/different-commits-between-two-branches) ================================================ FILE: git/list-filenames-without-the-diffs.md ================================================ # List Filenames Without The Diffs The `git show` command will list all changes for a given reference including the diffs. With diffs included, this can get rather verbose at times. If you just want to see the list of files involved (excluding the diffs), you can use the `--name-only` flag. For instance, ``` $ git show HEAD --name-only commit c563bafb511bb984c4466763db7e8937e7c3a509 Author: jbranchaud Date: Sat May 16 20:56:07 2015 -0500 This is my sweet commit message app/models/user.rb README.md spec/factories/user.rb ``` ================================================ FILE: git/list-just-the-files-involved-in-a-commit.md ================================================ # List Just The Files Involved In A Commit As part of a script I was writing I needed a command that could list out all the files involved in a given commit. The `git-show` command (which is considered a porcelain command) can do this but isn't ideal for scripting. It is recommended to instead use _plumbing_ commands. The `git-diff-tree` command is perfect for listing blobs (files) involved in a commit. ```bash $ git diff-tree --no-commit-id --name-only -r ``` The `--no-commit-id` flag suppresses the output of the commit id, because we want just the files. The `--name-only` flag tells the command to suppress other file info and to only show the file names. The `-r` flag tells the command to recurse into subtrees so that you get full pathnames instead of just the top-level directory. If you're interested, the `git-show` parallel to this is: ```bash $ git show --pretty="" --name-only ``` See `man git-diff-tree` and `man git-show` for more details on these. [source](https://stackoverflow.com/questions/424071/how-to-list-all-the-files-in-a-commit) ================================================ FILE: git/list-most-git-commands.md ================================================ # List Most Git Commands You can list most git commands by using the `-a` flag with `git-help`: ``` $ git help -a ``` [Source](http://stackoverflow.com/questions/7866353/git-list-all-available-commands) ================================================ FILE: git/list-untracked-files-for-scripting.md ================================================ # List Untracked Files For Scripting You'll generally run `git status` to get an overview of the index and working tree of a git project. This is a _porcelain_ command meant for a Git end-user. If you want to do some scripting, you'll want a _plumbing_ command like `ls_files`. The `git ls-files` command will > Show information about files in the index and the working tree This command can be used to list all untracked files in the working tree with two flags. 1. The `--others` flag will show untracked files in the output 2. The `--exclude-standard` will use the standard ignore files like `.gitignore` and `.git/info/exclude`. Put it all together and you've got: ```bash $ git ls-files --others --exclude-standard ``` In [Make One-Line Commands Interactive with fzf](https://www.youtube.com/watch?v=wf5eXdwfVws), I show how to use this with `fzf` to interactively remove untracked files that are no longer wanted. [source](https://stackoverflow.com/a/3801554/535590) ================================================ FILE: git/list-untracked-files.md ================================================ # List Untracked Files Git's `ls-files` command is a plumbing command that allows you to list different sets of files based on the state of the working directory. So, if you want to list all untracked files, you can do: ``` $ git ls-files --others ``` This includes files *ignored* by the `.gitignore` file. If you want to exclude the ignored files and only see *untracked* and *unignored* files, then you can do: ``` $ git ls-files --exclude-standard --others ``` There are some other flags worth checking at at `git help ls-files`. [source](http://stackoverflow.com/questions/2657935/checking-for-a-dirty-index-or-untracked-files-with-git) ================================================ FILE: git/move-the-latest-commit-to-a-new-branch.md ================================================ # Move The Latest Commit To A New Branch I sometimes find myself making a commit against the `master` branch that I intended to make on a new branch. To get this commit on a new branch, one possible approach is to do a reset, checkout a new branch, and then re-commit it. There is a better way. ```bash $ git checkout -b my-new-branch $ git checkout - $ git reset --hard HEAD~ ``` This makes better use of branches and avoids the need to redo a commit that has already been made. Note: The example was against the `master` branch, but can work for any branch. ================================================ FILE: git/override-the-global-git-ignore-file.md ================================================ # Override The Global Git Ignore File One of the places that `git` looks when deciding whether to pay attention to or ignore a file is in your global _ignore_ file. By default, `git` will look for this file at `$XDG_CONFIG_HOME/git/ignore` or `$HOME/.config/git/ignore`. I don't have `$XDG_CONFIG_HOME` set on my machine, so it will fall back to the config directory under `$HOME`. I may have to create the `git` directory and `ignore` file. ```bash $ mkdir $HOME/.config/git $ touch $HOME/.config/git/ignore ``` Then I can add file and directories to exclude to that `ignore` file just like I would any other `.gitignore` file. If I'd prefer for the global _ignore_ file to live somewhere else, I can specify that location and filename in my `$HOME/.gitconfig` file. ``` [core] excludesFile = ~/.gitignore ``` Setting this will override the default, meaning the default file mentioned above will be ignored ("now you know how it feels, ignore file!"). In this case, I'll need to create the `.gitignore` file in my home directory and add any of my ignore rules. [source](https://git-scm.com/docs/gitignore) ================================================ FILE: git/pick-specific-changes-to-stash.md ================================================ # Pick Specific Changes To Stash If you run `git stash`, all of the changes to tracked files on the working tree will be put into a commit-like entity in the stash list. If you want to be a bit choosier about what gets saved during a stash, you can include the `--patch` flag. > With --patch, you can interactively select hunks from the diff between HEAD > and the working tree to be stashed. ```bash $ git stash --patch ``` Once in the interactive mode initiated by `--patch`, you'll be presented with a change of changes and some options. You hit `y` for "yes, include this" and `n` for "no, don't include that". And then there are some more advanced options which you can read about in the _Interactive Mode_ section of `git-add`'s man page. See `man git-stash` for more details. ================================================ FILE: git/pulling-in-changes-during-an-interactive-rebase.md ================================================ # Pulling In Changes During An Interactive Rebase My standard workflow when doing feature development is to checkout a feature branch and commit changes as I go. When the feature is finished, I clean up the commit history with an interactive rebase and then integrate those changes with `master`. I initiate the interactive rebase like this (while on the feature branch): ``` $ git rebase -i master ``` This allows me to squash, fixup, and delete commits that I've made since checking out this branch from `master`. It is important to note that an another thing will happen seemingly behind the scenes. Any commits on `master` since the feature branch was checked out will be applied to the feature branch before the effects of the interactive rebase are applied. If you want to strictly do an interactive rebase of the commits on the feature branch ignoring what is on `master`, then reference the commit you checked out from -- put another way, reference the commit before the first commit on this branch. ``` $ git rebase -i ~ ``` The tilde (`~`) will go back one commit from the specified commit sha. See `man git-rebase` for more details. ================================================ FILE: git/push-to-a-branch-on-another-remote.md ================================================ # Push To A Branch On Another Remote The kind of `git-push` I do most often is: ```bash $ git push origin HEAD ``` which I have aliased to `put` in my `.gitconfig`. `HEAD` generally refers to whatever branch you currently have checked out. So this command will take the state of your current branch and push it to the branch of the same name on the `origin`, which is a _remote_ (see `git remote -v`). If you have other remotes set up, such as a `staging`, `heroku`, etc., then you may instead want to push to one of those. ```bash $ git push heroku HEAD ``` This will push the state of the current branch to that branch on the `heroku` remote. If I instead want to push the changes from one local branch to a different named remote branch, then I have to specify both like so: ```bash $ git push heroku staging:main ``` This will push the state of my local `staging` branch to the `main` branch on the `heroku` remote. [source](https://devconnected.com/how-to-push-git-branch-to-remote/) ================================================ FILE: git/quicker-commit-fixes-with-the-fixup-flag.md ================================================ # Quicker Commit Fixes With The Fixup Flag Imagine you're working a feature branch and realize that the first commit you made had a typo. You could just tack on another commit to fix the typo, but that will add noise to your commit history. You can fix it up by making a _fixup_ commit. 1. Make your typo fix 2. Stage the fix 3. Find the SHA of the commit that you want to fix (e.g. `2ee53ad`) 4. Create a _fixup_ commit: `git commit --fixup 2ee53ad` This _fixup_ commit is tied to the original commit it is fixing. ``` ❯ git log --pretty=oneline --abbrev-commit b4258b6 (HEAD -> feature-branch) fixup! Add header 9c0d2b0 Different atomic change 2ee53ad Add header 8486b91 (master) Initial commit ``` To then apply the _fixup_, run `git rebase -i --autosquash master`. This will present you with the following _interactive rebase_ screen: ``` pick 2ee53ad Add header fixup b4258b6 fixup! Add header pick 9c0d2b0 Different atomic change # Rebase 8486b91..b4258b6 onto 8486b91 (3 commands) ``` Because git knows that your _fixup_ commit is tied to `2ee53ad`, it automatically moves it into place below that commit with the `fixup` command. Saving will apply and autosquash the fixup commit leaving you with a clean commit history. ================================================ FILE: git/rebase-commits-with-an-arbitrary-command.md ================================================ # Rebase Commits With An Arbitrary Command Interactive rebasing is a powerful way to manage and tend to the history of a git repository. Rewording and squashing commits are fairly common actions. But what if you need to run some arbitrary command against a series of recent commits? This is where the `--exec` flag comes in to play. ```bash $ git rebase -i HEAD~3 --exec "git commit --amend --reset-authors -CHEAD" ``` This generates an interactive rebase file that you can review and save when ready. ``` pick ea4a215 Add Globally Install A Package With Yarn as a javascript til exec git commit --amend --reset-author -CHEAD pick a4f4143 Add Initialize A New JavaScript Project With Yarn as a javascript til exec git commit --amend --reset-author -CHEAD pick 2f00aeb Add Default And Named Exports From The Same Module as a javascript til exec git commit --amend --reset-author -CHEAD ``` As you can see, the specified command is prepared for execution for each commit involved in the rebase. h/t [Patricia Arbona](https://github.com/arbonap) ================================================ FILE: git/reference-a-commit-via-commit-message-pattern-matching.md ================================================ # Reference A Commit Via Commit Message Pattern Matching Generally when referencing a commit, you'll use the SHA or a portion of the SHA. For example with `git-show`: ``` $ git show cd6a63d014 ... ``` There are many ways to reference commits though. One way is via regex pattern matching on the commit message. For instance, if you recently had a commit with a typo and you had included *typo* in the commit message, then you could reference that commit like so: ``` $ git show :/typo Author: Josh Branchaud Date: Mon Dec 21 15:50:20 2015 -0600 Fix a typo in the documentation ... ``` By using `:/` followed by some text, git will attempt to find the most recent commit whose commit message matches the text. As I alluded to, regex can be used in the text. See `$ man gitrevisions` for more details and other ways to reference commits. [Source](https://twitter.com/jamesfublo/status/678906346335428608) ================================================ FILE: git/reference-commits-earlier-than-reflog-remembers.md ================================================ # Reference Commits Earlier Than Reflog Remembers While preparing some stats for a recent blog post on [A Decade of TILs](https://www.visualmode.dev/a-decade-of-tils), I ran into an issue referencing chunks of time further back than 2020. ```bash ❯ git diff --diff-filter=A --name-only HEAD@{2016-02-06}..HEAD@{2017-02-06} -- "*.md" warning: log for 'HEAD' only goes back to Sun, 20 Dec 2020 00:26:27 -0600 warning: log for 'HEAD' only goes back to Sun, 20 Dec 2020 00:26:27 -0600 ``` This is because `HEAD@...` is a reference to the `reflog`. The `reflog` is a local-only log of objects and activity in the repository. That date looks suspiciously like the time that I got this specific machine and cloned the repo. In order to access this information, I need a different approach of finding references that bound these points in time. How about asking `rev-list` for the first commit it can find before the given dates in 2017 and 2016 and then using those. ```bash ❯ git rev-list -1 --before="2017-02-07 00:00" HEAD 17db6bc4468616786a8f597a10d252c24183d82e ❯ git rev-list -1 --before="2016-02-07 00:00" HEAD f1d3d1f796007662ff448d6ba0e3bbf38a2b858d ❯ git diff --diff-filter=A --name-only f1d3d1f796007662ff448d6ba0e3bbf38a2b858d..17db6bc4468616786a8f597a10d252c24183d82e -- "*.md" # git outputs a bunch of files ... ``` ================================================ FILE: git/remove-untracked-files-from-a-directory.md ================================================ # Remove Untracked Files From A Directory Let's say I have a directory (`spec/cassettes`) full of a ton of generated YAML files. Most of these files are tracked by git. However, I just generated a bunch of new ones that are untracked. For whatever reason, I don't want these files. I need to delete them. Running `rm` on each of them is going to be too tedious. And it is tricky to target them for a bulk delete since there are a ton of other files in that directory that I want to keep. One way to approach this is have `git ls-files` help out with listing all files in the directory that are untracked. The `--others` flag filters to untracked files. ```bash git ls-files --others --exclude-standard spec/cassettes ``` From there, I can pipe it to `rm` (with `xargs` collapsing all the files into a single line): ```bash git ls-files --others --exclude-standard spec/cassettes | xargs rm ``` See `man git-ls-files` for more details. ================================================ FILE: git/rename-a-remote.md ================================================ # Rename A Remote If you just added a remote (`git remote add ...`) and messed up the name or just need to rename some existing remote, you can do so with the `rename` command. First, let's see the remotes we have: ```bash $ git remote -v origin https://github.com/jbranchaud/til.git (fetch) origin https://github.com/jbranchaud/til.git (push) ``` To then rename `origin` to `destination`, for example, we can issue the following command: ```bash $ git remote rename origin destination ``` See `man git-remote` for more details. ================================================ FILE: git/renaming-a-branch.md ================================================ # Renaming A Branch The `-m` flag can be used with `git branch` to move/rename an existing branch. If you are already on the branch that you want to rename, all you need to do is provide the new branch name. ```bash $ git branch -m ``` If you want to rename a branch other than the one you are currently on, you must specify both the existing (old) branch name and the new branch name. ```bash $ git branch -m ``` h/t Dillon Hafer ================================================ FILE: git/resetting-a-reset.md ================================================ # Resetting A Reset Sometimes we run commands like `git reset --hard HEAD~` when we shouldn't have. We wish we could undo what we've done, but the commit we've *reset* is gone forever. Or is it? When bad things happen, `git-reflog` can often lend a hand. Using `git-reflog`, we can find our way back to were we've been; to better times. ```bash $ git reflog 00f77eb HEAD@{0}: reset: moving to HEAD~ 9b2fb39 HEAD@{1}: commit: Add this set of important changes ... ``` We can see that `HEAD@{1}` references a time and place before we destroyed our last commit. Let's fix things by resetting to that. ```bash $ git reset HEAD@{1} ``` Our lost commit is found. Unfortunately, we cannot undo all the bad in the world. Any changes to tracked files will be irreparably lost. [source](http://stackoverflow.com/questions/2510276/undoing-git-reset) ================================================ FILE: git/resolve-a-merge-conflict-from-stash-pop.md ================================================ # Resolve A Merge Conflict From Stash Pop Occasionally when I've stashed some changes and then `stash pop` them back onto me working tree, I'll end up with a merge conflict. Either some new commits or other changes to the working tree are in conflict with what's in the stash. Git tells me it's up to me to resolve it. Running `git status`, I can see what files have merge conflicts. I can go and resolve those now based on what I want the state of my working tree to be. Running `git status`, I'll also note that the files from the stash not involved in the conflict have been staged. I don' know why git stages those changes when a merge conflict arises. When I `stash pop` and there is no merge conflict, git leaves the changes on the working tree. This has confused me more than a couple times. Is there anything I need to do to complete or resolve the `stash pop`? Nope. Resolving the conflicts in the affected files is all that needs to be done. And since I'm generally not looking to create a commit at this point, I _unstage_ the staged changes with `git reset HEAD`. ================================================ FILE: git/restore-file-from-one-branch-to-the-current.md ================================================ # Restore File From One Branch To The Current On one feature branch I have created some files and made changes to some existing files as part of spiking a feature. Now I'm on a different branch taking another shot at it. I want changes from one or two of the files. In the past I've used `git-checkout` for this task. However, I believe this is one of the use cases they had in mind when they added `git-restore`. What I want to do is _restore_ the state of a file as it appears on some source branch to my current branch. Here is what that looks like: ```bash $ git restore --source=some-feature-branch app/models/contact.rb ``` Now when I check `git status` I'll see the state of that file on the `some-feature-branch` branch overlayed on my current working copy. If the file doesn't exist, it will be created. See `man git-restore` for more details. ================================================ FILE: git/review-commits-from-before-a-certain-date.md ================================================ # Review Commits From Before A Certain Date I was recently looking at data in a 3rd-party tool and saw that there was a very distinct shift in what was being recorded a couple years prior on a specific date. I wanted to see what changes had been made to the codebase a day or two before the shift. Rather than scrolling all the way back in `git log`, I can tell `git log` to show me all commits from before a certain date. Let's say that date of interest is May 1st, 2021. I can use the `--until` flag with `git log`. However, I should note that `--until` is an exclusive range, so I'll need to specify `May 2 2021` if I want to start seeing commits on May 1. ```bash $ git log --until='May 2 2021' ``` Because `git log` shows commits in reverse chronological order, I'll start seeing commits from May 1st and then as I scroll, I'll see older and older commits. From here I can scan commits messages and look for one that I want to dig into. I'd then use `git show ` to explore a specific one further. This is synonymous with `--before`. See `man git-log` for more details. ================================================ FILE: git/run-a-git-command-from-outside-the-repo.md ================================================ # Run A Git Command From Outside The Repo Generally you run a git command from somewhere within the parent folder where the `.git` directory lives. Git recognizes the `.git` directory in that parent directory and runs your command against it. You can run a command against a given git repository without being within the parent directory. This can be handy for scripting as well as for one-off commands when you don't want to `cd` to the directory. To do this, you need to tell Git where to find the `.git` directory. You do this with the `-C` flag. For instance, from anywhere on my machine, I can view a log of this TIL repository with the following: ```bash $ git -C ~/code/til log ``` Notice that the `-C` flag and its argument are positioned directly after `git`. The command (`log`) should be positioned after that. See `man git` for more details. [source](https://stackoverflow.com/a/35899275/535590) ================================================ FILE: git/set-a-custom-pager-for-a-specific-command.md ================================================ # Set A Custom Pager For A Specific Command The pager can be [configured globally](configuring-the-pager.md), for [one run of a command](turn-off-the-output-pager-for-one-command.md), or as I'll explain in this post, for a specific command. _I explore all of this in [Optimize the way Git displays the output of commands](https://www.youtube.com/watch?v=VpFldePcu_w)._ Let's assume a git configuration that uses `less` for any command that need a pager. Perhaps you'd like for the `git show` to work a bit differently than other commands. You want it to use `less` with the `-F` and `-X` flags. A custom pager command can be set for any command in the `[pager]` section of the `~/.gitconfig` file. ``` [pager] show = "less -FX" ``` If you want to turn off the pager for a specific command, set it to the boolean value `false` instead. ``` [pager] show = false ``` See `man git-config` for more details in the `core.pager` and `pager.` sections. ================================================ FILE: git/set-default-branch-name-for-new-repos.md ================================================ # Set Default Branch Name For New Repos When you run `git init` in a directory, it will initialize that directory as a `git` repository. The default branch name for any new git repository is `master`. A better name for the base branch would be something like `main`. To set `main` as the new default for all new repos, you can run the following `git` command. ```bash $ git config --global init.defaultBranch main ``` This will update your `.gitconfig` file to contain the following lines. ``` [init] defaultBranch = main ``` Try running `git init` in a fresh directory and then `git branch --show-current` to see that the base branch is now defaulting to `main`. ================================================ FILE: git/set-up-gpg-signing-key.md ================================================ # Set Up GPG Signing Key I wanted to have that "Verified" icon start showing up next to my commits in GitHub. To do that, I need to generate a GPG key, configure the secret key in GitHub, and then configure the public signing key with my git config. ```bash # generate a gpg key $ gpg --full-generate-key # Pick the following options when prompted # - Choose "RSA and RSA" (Options 1) # - Max out key size at 4096 # - Choose expiration date (e.g. 0 for no expiration) # - Enter "Real name" and "Email" (I matched those to what is in my global git config) # - Set passphrase (I had 1password generate a 4-word passphrase) ``` It may take a few seconds to create. I can see it was created by listing my GPG keys. ```bash $ gpg --list-secret-keys --keyid-format=long [keyboxd] --------- sec rsa4096/1A8656918A8D016B 2025-10-19 [SC] ... ``` I'll need the `1A8656918A8D016B` portion of that response for the next command and it is what I set as my public signing key in my git config. First, though, I add the full key block to my GitHub profile which I can copy like so: ```bash $ gpg --armor --export 1A8656918A8D016B | pbcopy ``` And then I paste that as a new GPG Key on GitHub under _Settings_ -> _SSH and GPG Keys_. Last, I update my global git config with the signing key and the preference to sign commits: ```bash git config --global user.signingkey 1A8656918A8D016B git config --global commit.gpgsign true ``` Without `commit.gpgsign`, I would have to specify the `-S` flag every time I want to create a signed commit. [source](https://git-scm.com/book/ms/v2/Git-Tools-Signing-Your-Work) ================================================ FILE: git/shorthand-to-force-push-a-branch.md ================================================ # Shorthand To Force Push A Branch If your local version of a branch differs in its history from the matching remote branch, then git will prevent you from pushing. You can override the difference on the remote by force pushing. One way of doing that is with the `--force` flag. ```bash $ git push --force origin main ``` There is a shorthand for this. [Prefix the branch name with a `+`](https://git-scm.com/docs/git-push#Documentation/git-push.txt---force). ```bash $ git push origin +main ``` When working in a team context, it is typically a safer bet to use `--force-with-lease` instead of force. That way if the remote contains new changes that you haven't pulled down yet, you will prevent yourself from accidentally overriding them. If you feel you must use `--force`, double check what will happen. Avoid accidentally clobbering work that could be hard or impossible to recover. [source](https://twitter.com/jbrancha/status/1558861987374780416?s=20&t=D7T_aTBaF97AwOvUnz9Muw) ================================================ FILE: git/show-all-commits-for-a-file-beyond-renaming.md ================================================ # Show All Commits For A File Beyond Renaming By including `-- ` with a `git log` command, we can list all the commits for a file. The following is an example of such a command with some formatting and file names. ```bash > git log --name-only --pretty=format:%H -- README.md 4e57c5d46637286731dc7fbb1e16330f1f3b2b7c README.md 56955ff027f02b57212476e142a97ce2b7e60efe README.md 5abdc5106529dd246450b381f621fa1b05808830 README.md ``` What we may not realize, though, is that we are missing out on a commit in this file's history. At one point, this file was renamed. The command above wasn't able to capture that. Using the `--follow` flag with a file name, we can list all commits for a file beyond renaming. ```bash > git log --name-only --pretty=format:%H --follow README.md 4e57c5d46637286731dc7fbb1e16330f1f3b2b7c README.md 56955ff027f02b57212476e142a97ce2b7e60efe README.md 5abdc5106529dd246450b381f621fa1b05808830 README.md ea885f458b0d525f673623f2440de9556954c0c9 README.rdoc ``` This command roped in a commit from when `README.md` used to be called `README.rdoc`. If you want to know about the *full* history of a file, this is the way to go. [source](http://stackoverflow.com/questions/3701404/list-all-commits-for-a-specific-file) ================================================ FILE: git/show-changes-for-files-that-match-a-pattern.md ================================================ # Show Changes For Files That Match A Pattern The `git show` command allows you to view the changes associated with a reference, such as a commit sha (e.g. `git show 86748aacf14e`). Consider a commit that has changed a bunch of JS files as well as two CSS files. If we run `git show abcd1234`, we will see all of the changes to each file which can result in quite a bit of noise. What if we only want to view the changes to the CSS files? We can instruct the command to only show changes to files that match a pattern by tacking that pattern on to the end. ```bash $ git show abcd1234 *.css ``` Alternatively, we could scope the output of the command to the files that live in a certain directory. ```bash $ git show abcd1235 src/css ``` ================================================ FILE: git/show-changes-in-the-compose-commit-message-view.md ================================================ # Show Changes In The Compose Commit Message View When you execute `git commit`, you'll be dropped into your default editor and prompted to compose a commit message. By default you'll only see the status and names of the files involved in the commit. To also see the line-by-line-changes in this view, you'll want to commit in verbose mode. ```bash $ git commit --verbose ``` You can also set verbose mode as the default by updating your `~/.gitconfig` file. ``` [commit] verbose = true ``` [source](https://stackoverflow.com/questions/5875275/git-commit-v-by-default) ================================================ FILE: git/show-file-diffs-when-viewing-git-log.md ================================================ # Show File Diffs When Viewing Git Log Include the `-p` flag with the `git log` command to include the diffs along with the rest of the information for each commit. Here is an example of running this against this repository. ```bash $ git log -p commit b9809a329acd8150b2474168f8faaf008f376e35 Author: jbranchaud Date: Wed Oct 11 07:27:53 2017 -0500 Add Inline Style Attributes Should Be Camel Cased as a react til diff --git a/README.md b/README.md index c982f8e..6ee7d32 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ smart people at [Hashrocket](http://hashrocket.com/). For a steady stream of TILs from a variety of rocketeers, checkout [til.hashrocket.com](https://til.hashrocket.com/). -_574 TILs and counting..._ +_575 TILs and counting..._ --- ``` See `man git-log` for more details. ================================================ FILE: git/show-list-of-most-recently-committed-branches.md ================================================ # Show List Of Most Recently Committed Branches The standard way to list your branches is with the `git branch` command. If you use branches extensively for feature work and bug fixes, you may find yourself overwhelmed by the list of branches trying to visually parse through them for the one that you had worked on recently. With the `git for-each-ref` command, we can produce a better list of branches. ```bash $ git for-each-ref --sort=-committerdate --count=10 --format='%(refname:short)' refs/heads/ ``` The command itself will iterate over all of the repository's refs and print them out as a list. The `--sort=-committerdate` option will ensure that list is sorted by refs mostly recently committed to. The `--count=10` option limits the list output to 10 refs. The `format` flag cleans up the output a bit, only showing the shortname of the ref. Lastly, the `refs/heads/` argument ensures that only local refs are included in the output, thus ignoring remote refs. The result is a list of local branches ordered by recency which generally corresponds to relevance. See `man git-for-each-ref` for more details. [source](https://twitter.com/djm_/status/961289486721339392) ================================================ FILE: git/show-only-commits-that-touch-specific-lines.md ================================================ # Show Only Commits That Touch Specific Lines When you run `git log`, you are listing all commits in reverse-chronological order for the current branch. There are ways of filtering the commits that get output from `git-log`. As of Git 1.8.4, `git-log` output can be filtered by commits that touch a range of line numbers. This is done with the `-L` flag. For instance, if I want to see all commits that touched the 13th line of my `README.md` file, then I can do this: ```bash $ git log -L13,13:README.md ``` I can alter the command to show commits that touched a range of lines like so: ```bash $ git log -L19,45:README.md ``` I used the `-L` flag recently to find when a dependency was added to my `package.json` file even though the most recent changes to that line were version bumps. [source](https://stackoverflow.com/questions/8435343/retrieve-the-commit-log-for-a-specific-line-in-a-file) ================================================ FILE: git/show-summary-stats-for-current-branch.md ================================================ # Show Summary Stats For Current Branch When I push a branch up to GitHub as a PR, there is a part of the UI that shows you how many lines you've added and removed for this branch. It bases that off the target branch which is typically your `main` branch. The `git diff` command can provide those same stats right in the terminal. The key is to specify the `--shortstat` flag which tells `git` to exclude other diff output and only show: - Number of files changed - Number of insertions - Number of deletions Here is the summary stats for a branch I'm working on: ```bash ❯ git diff --shortstat main 8 files changed, 773 insertions(+), 25 deletions(-) ``` We have to be on our feature branch and then we point to the branch (or whatever ref) we want to diff against. Since I want to know how my feature branch compares to `main`, I specify that. See `man git-diff` for more details. ================================================ FILE: git/show-the-diffstat-summary-of-a-commit.md ================================================ # Show The diffstat Summary Of A Commit Use the `--stat` flag when running `git show` on a commit to see the `diffstat` summary of that commit. For instance, this is what I get for a recent commit to [delve](https://github.com/derekparker/delve): ```bash $ git show 8a1f36a1ce --stat commit 8a1f36a1ce71d708d1d82afbc2191de9aefba021 Author: Derek Parker Date: Wed Jan 27 23:47:04 2016 -0800 dlv: Flag to print stacktrace on trace subcommand cmd/dlv/main.go | 45 ++++++++++----------------------------------- terminal/command.go | 7 +++++-- 2 files changed, 15 insertions(+), 37 deletions(-) ``` The following is a description of the `diffstat` program > This program reads the output of diff and displays a histogram of the > insertions, deletions, and modifications per-file. See `man git-show` and `man diffstat` for more details. ================================================ FILE: git/show-the-good-and-the-bad-with-git-bisect.md ================================================ # Show The Good And The Bad With Git Bisect The [`git bisect`](https://git-scm.com/docs/git-bisect) command is a powerful tool for tracking down a past commit where something verifiable about the code changed -- whether it be visual or functional. After using `git bisect` to traverse back and forth through your commit history, you may be wondering where things stand. The [`git bisect log`](https://git-scm.com/docs/git-bisect#_bisect_log_and_bisect_replay) command will show you each commit that has been inspected and whether you've marked it as good or bad. These records can be handy for double checking your work if you're worried that you made a mistake along the way. ================================================ FILE: git/show-what-is-in-a-stash.md ================================================ # Show What Is In A Stash Usually when I want to inspect anything in git, I'll use `git show` with a specific ref. This can even be done with stash refs. ```bash $ git stash list stash@{0}: WIP on ... stash@{1}: Some commit on ... $ git show stash@{0} # ... ``` The `git-stash` command has a built-in way of showing stashes that will save you from having to type out the somewhat awkward `stash@{n}` ref. ```bash $ git stash show 1 ``` This will show you the `stash@{1}` ref. You can also omit a number which will show you the latest stash (`stash@{0}`). See `man git-stash` for more details. ================================================ FILE: git/single-key-presses-in-interactive-mode.md ================================================ # Single Key Presses in Interactive Mode When staging changes in interactive mode (`git add -p`), you have a number of options associated with single keys. `y` is *yes*, `n` is *no*, `a` is *this and all remaining*, and so on. By default, you have to press *enter* after making your selection. However, it can be configured for single key presses. Add the following to your git configuration file to enable single key presses for interactive mode: ``` [interactive] singlekey = true ``` [source](https://github.com/hashrocket/dotmatrix/blob/master/.gitconfig#L33-L34) ================================================ FILE: git/skip-a-bad-commit-when-bisecting.md ================================================ # Skip A Bad Commit When Bisecting The `git bisect` command helps you quickly track down the commit where a bug was introduced. It is quick because it picks the optimal (minimal) commits in a binary search fashion. It is possible that `git bisect` will pick a commit that you aren't able to evaluate as _good_ or _bad_. If that commit is in a WIP state or for some other unrelated reason prevents you from evaluating it, then you are kinda stuck. To move forward, tell `git bisect` that you want to skip this commit: ```bash $ git bisect skip ``` It will flag that one as skipped and find you another nearby commit to evaluate instead. If your commit history is in such a state that you have to skip many of the suggested commits, it is possible that `git bisect` will not be able to help you identify the problem commit. You may be left with a few commits that you'll have to manually read through and evaluate. This is a good reason to keep your commits atomic and in a functional state. [source](https://git-scm.com/docs/git-bisect#_bisect_skip) ================================================ FILE: git/skip-git-hooks-as-needed.md ================================================ # Skip Git Hooks As Needed Projects have Git hooks configured for all sorts of reasons. Most common are `pre-commit` hooks which verify certain aspects of the contents of a commit. A `pre-commit` hook could check that the tests all pass, that the changes don't include any debugging statements, and so forth. There are all kinds of hooks though, like `pre-rebase` and `post-checkout`. These hooks can sometimes get in the way and we may need to skip or disable them on a one-off basis. Several Git commands offer a `--no-verify` flag which can skip running the hook associated with that command. - `git commit --no-verify` (skips `pre-commit` and `commit-msg` hooks) - `git push --no-verify` (skips `pre-push` hook) - `git merge --no-verify` (skips `pre-merge-commit` hook) - `git am --no-verify` (skips `applypatch-msg` and `pre-applypatch` hooks) If you look in the `.git/hooks` directory, there are several other hooks not covered by the above. So, what if I am doing an action like `git checkout` and I want to skip the `post-checkout` hook? I can override the `hooksPath` config for that one command with the `-c` flag. ```sh $ git -c core.hooksPath=/dev/null checkout ... ``` By setting it to `/dev/null`, it will find *no* hooks available, so none will be executed for this command. See `man git-config` for more details on `core.hooksPath`. ================================================ FILE: git/skip-pre-commit-hooks.md ================================================ # Skip Pre-Commit Hooks Projects can choose to adopt pre-commit hooks as part of their contribution workflow. These hooks can help enforce project standards like ensuring a set of changes are formatted and linting properly. These can be set up with a tool like [husky](https://github.com/typicode/husky) or with a custom script. As you're working on a feature branch, you can and should make frequent checkpoint commits like a climber puts [pitons](https://en.wikipedia.org/wiki/Piton) into the rock face. These are anchor points that reduce the risk of losing work. They make it easier and safer to return to a point in time when your code was in a "good" state. If your checkpoint commit isn't conforming to all the pre-commit hook checks, you can choose to skip the checks and commit anyway. To do this, tack on the `--no-verify` flag. ```bash $ git commit --no-verify ``` With this checkpoint in place, you can either plunge forward with the feature or you can even go fix the pre-commit violations and combine them into (`--amend`) that checkpoint commit. Don't abuse this. You still want the overall work to conform to project guidelines. Use the process that works best for you as you get there. See `man git-commit` for more details. ================================================ FILE: git/staging-changes-within-vim.md ================================================ # Staging Changes Within Vim I've always used git from the command line, but only recently have I started to leverage [fugitive.vim](https://github.com/tpope/vim-fugitive) to more quickly do some common git commands from within vim. I mostly use *fugitive* to stage changes for committing. To stage entire files, you can view the repository status, `:Gstatus`, and then navigate up and down (`k` and `j`) tapping `-` next to the files to be staged (or unstaged). I've started to use git's interactive mode for staging changes from the command line (`git add --patch`) more and more and recently wondered if the same thing can be accomplished with *fugitive*. It turns out it's pretty simple to do so. Instead of tapping `-` next to a file you want to stage, you can tap `p` next to it and you will be immediately dropped into interactive mode for that file. Prepare the lines you want to stage (or, again, unstage) and save. ================================================ FILE: git/staging-stashes-interactively.md ================================================ # Staging Stashes Interactively The `-p` flag can be used with `git stash`, just as it is used with `git add`, for interactively staging a stash. So, if you have changes in your working tree that you want to stash mixed with ones that you want to keep working with, then you can simply do: ``` git stash -p ``` Read more on [interactive staging](https://git-scm.com/book/en/v2/Git-Tools-Interactive-Staging). ================================================ FILE: git/stash-a-single-untracked-file.md ================================================ # Stash A Single Untracked File If you want to stash everything in your work tree and untracked files, you can run: ```bash $ git stash -u ``` If you want a bit more control over what gets stashed from the work tree, you can interactively stash with `--patch` (or `-p`): ```bash $ git stash -p ``` Unfortunately, the two don't work together. ```bash $ git stash -u -p Can't use --patch and --include-untracked or --all at the same time ``` So, if you'd like to stash a specific untracked file, you can instead formulate a command like the following: ```bash $ git stash -u -- ``` This will stash just the specified untracked file and leave the rest of them as they are. I found this useful when trying to test the setup of a new library. There was an extra new file that I didn't think I needed. Stashing it temporarily gets it out of the way without losing it. ================================================ FILE: git/stash-everything.md ================================================ # Stash Everything Running the `git stash` command will take all the changes to tracked files in the working directory and stash them away. You can tack on the `--include-untracked` flag to make sure that untracked files are also included in the stash. You can take this a step further with the `--all` flag. This will stash everything included files that you've told git to ignore. You probably don't want to do this. I ran this command and realized after the command hung for about 10 seconds that I had just stashed the `node_modules` directory. See `man git-stash` for more details. ================================================ FILE: git/stashing-only-unstaged-changes.md ================================================ # Stashing Only Unstaged Changes If you have both staged and unstaged changes in your project, you can perform a stash on just the unstaged ones by using the `-k` flag. The staged changes will be left intact ready for a commit. ``` $ git status On branch master ... Changes to be committed: modified: README.md Changes not staged for commit: modified: app/models/user.rb $ git stash -k Saved working directory and index state ... $ git status On branch master ... Changes to be committed: modified: README.md ``` h/t [Chris Erin](https://twitter.com/MCNormalMode) ================================================ FILE: git/stashing-untracked-files.md ================================================ # Stashing Untracked Files Normally when stashing changes, using `git stash`, git is only going to stash changes to *tracked* files. If there are any new files in your project that aren't being tracked by git, they will just be left lying around. You might not want these *untracked* files left behind, polluting your working copy. Perhaps these files change your projects functionality or affect the outcome of tests. You just want them out of the way, for the moment at least. And so along comes the `-u` or `--untracked` flag. ```bash $ touch new_file.rb $ git stash No local changes to stash $ git stash -u Saved working directory and index state WIP ... ``` ================================================ FILE: git/switch-to-a-recent-branch-with-fzf.md ================================================ # Switch To A Recent Branch With FZF [Git](https://git-scm.com/) and [FZF](https://github.com/junegunn/fzf) make a powerful team. Once you've installed FZF on your machine, there are all kinds of add-on scripts that you can include in your shell configuration. This one, for instance, is a personal favorite. ```bash fbr() { local branches branch branches=$(git for-each-ref --count=30 --sort=-committerdate refs/heads/ --format="%(refname:short)") && branch=$(echo "$branches" | fzf-tmux -d $(( 2 + $(wc -l <<< "$branches") )) +m) && git checkout $(echo "$branch" | sed "s/.* //" | sed "s#remotes/[^/]*/##") } ``` When you run the `fbr` command from your shell, FZF will compile a list of the most recently committed to branches (including remote branches). That list will then be available in a standard FZF prompt so that you can choose the branch you want to switch to. [source](https://github.com/junegunn/fzf/wiki/examples) ================================================ FILE: git/transition-a-branch-from-one-base-to-another.md ================================================ # Transition A Branch From One Base To Another Let's say I'm working in a git workflow where we have `main` as our production branch and `dev` as our development branch. I've opened a feature branch off of `main` and made a series of commits. It is at that point that I realize I should instead be branched off `dev`. I could open up a new branch off `dev` and then `cherry-pick` those commits. That is messier and more steps than necessary. Instead, I can transition the feature branch I'm already working from to a different base. The [`git-rebase`](https://git-scm.com/docs/git-rebase) command supports this with the `--onto` flag. ```bash $ git rebase --onto dev main my-feature-branch ``` Specify the new base branch (`dev`), the current base (`main`), and then the name of the branch you are transitioning (`my-feature-branch`). [source](https://stackoverflow.com/a/10853956/535590) ================================================ FILE: git/turn-off-the-output-pager-for-one-command.md ================================================ # Turn Off The Output Pager For One Command With `git` a pager, such as `less`, can be configured to display paginated command output. There are many ways to set up the default pager such as setting the `core.pager` value in your git-config or by setting the `$PAGER` env var. Assuming it is set to something like `less`, you can view, scroll through, and search the output of a command like `git log` or `git diff`. When you're finished the pager will close, all the output will vanish, and you'll be back at your terminal prompt. This is generally a desirable workflow. If, however, you want to be able to scroll back in your terminal history to reference a SHA or a commit message, you'll be disappointed. For one off commands where you know you'll want the output actually printed to the terminal, you can turn off the pager with the `--no-pager` flag (or `-P` as a shorthand). ```bash $ git --no-pager show ``` This will print the details of the HEAD commit to the terminal. I can scroll back and reference them as needed. [source](https://stackoverflow.com/questions/2183900/how-do-i-prevent-git-diff-from-using-a-pager#:~:text=17%20Answers&text=%2D%2Dno%2Dpager%20to%20Git,fits%20in%20a%20single%20screen.&text=As%20a%20previous%20answer%20mentioned,is%20less%20than%20one%20screen.) ================================================ FILE: git/two-kinds-of-dotted-range-notation.md ================================================ # Two Kinds Of Dotted Range Notation There are two kinds of dotted range notation in git -- `..` and `...`. If you'd like to view all changes on your current feature branch since checking out from `master`, you can use the two-dot notation: ```bash ❯ git log master..some-feature-branch --oneline 9e50bff (some-feature-branch) Add second feature change b11bb0b Add first feature change ``` You could also switch the refs around to see what has changed on master since checking out: ```bash ❯ git log some-feature-branch..master --oneline c2880f8 (HEAD -> master) Add description to README ``` Then there is the three-dot notation. This will include all commits from the second ref that aren't in the first and all commits in the first that aren't in the second. ```bash ❯ git log master...some-feature-branch --oneline c2880f8 (HEAD -> master) Add description to README 9e50bff (some-feature-branch) Add second feature change b11bb0b Add first feature change ``` See `man git-rev-parse` for more details. [source](https://stackoverflow.com/a/24186641/535590) ================================================ FILE: git/undo-latest-changes-committed-to-specific-file.md ================================================ # Undo Latest Changes Committed To Specific File I'm reviewing the changes I've made in a PR before I request a review from my team. There are a scattering of changes in one file that I've changed my mind on. Everything else looks good though. So, I need to undo the changes in that file before proceeding. Manually undoing them is going to be clunky. There is a way to do it with `git checkout`, but that is one of the ways in which `git-checkout` was overloaded leading to the release of `git-restore`. Let's use `git-restore` instead. By specifying a `--source`, I can tell `git` what _ref_ in the commit history that file should be restored to. I'm on a short-lived feature branch, so pointing to `main` is good enough. ```bash $ git restore --source=main app/models/customer.rb ``` If I've changed a file at multiple points on this feature branch and I don't want to undo all of them, then pointing to `main` is no longer going to work. Instead, I can point to the commit right before the current one (`HEAD`) that I'm trying to undo. ```bash $ git restore --source=HEAD~ app/models/customer.rb ``` This really isn't much different than the `git-checkout` version, but I still find it to be a little clearer. ```bash $ git checkout HEAD~ -- app/models/customer.rb ``` See `man git-restore` for more details. ================================================ FILE: git/unstage-changes-with-git-restore.md ================================================ # Unstage Changes With Git Restore Git 2.23 introduced the `restore` command which is a more direct alternative to `checkout` and `reset` for restoring the state of the working tree and the index (staging area). With the `--staged` flag, we can unstage changes, moving them from the index to the working tree. > To restore a file in the index to match the version in HEAD (this is the same > as using git-reset(1)) ``` $ git restore --staged README.md ``` Staged changes to `README.md` will be removed from the index and put on the working tree. ``` $ git restore --staged . ``` That will unstage all changes on the index. This is now recommended by Git when you run `git status`: > (use "git restore --staged ..." to unstage) See `man git-restore` for more details. ================================================ FILE: git/untrack-a-directory-of-files-without-deleting.md ================================================ # Untrack A Directory Of Files Without Deleting In [Untrack A File Without Deleting It](untrack-a-file-without-deleting-it.md), I explained how a specific file can be removed from tracking without actually deleting the file from the local file system. The same can be done for a directory of files that you don't want tracked. Just use the `-r` flag: ```bash $ git rm --cached -r ``` [source](http://stackoverflow.com/questions/1143796/remove-a-file-from-a-git-repository-without-deleting-it-from-the-local-filesyste) ================================================ FILE: git/untrack-a-file-without-deleting-it.md ================================================ # Untrack A File Without Deleting It Generally when I invoke `git rm `, I do so with the intention of removing a file from the project entirely. `git-rm` does exactly that, removing the file both from the index and from the working tree. If you want to untrack a file (remove it from the index), but still have it available locally (in the working tree), then you are going to want to use the `--cached` flag. ```bash $ git rm --cached ``` If you do this, you may also consider adding that file to the `.gitignore` file. [source](http://stackoverflow.com/questions/15027873/untrack-and-stop-tracking-files-in-git) ================================================ FILE: git/update-the-url-of-a-remote.md ================================================ # Update The URL Of A Remote I just changed the name of a Github repository. One of the implications of this is that the remote URL that my local git repository has on record is now out of date. I need to update it. If I use `git-remote` with the `-v` flag. I can see what remotes I currently have. ```bash $ git remote -v origin git@github.com:jbranchaud/pokemon.git (fetch) origin git@github.com:jbranchaud/pokemon.git (push) ``` Now, to update the URL for that remote, I can use `git remote set-url` specifying the name of the remote and the updated URL. ```bash $ git remote set-url origin git@github.com:jbranchaud/pokemon_deluxe.git ``` If I check again, I can see it has been updated accordingly. ```bash $ git remote -v origin git@github.com:jbranchaud/pokemon_deluxe.git (fetch) origin git@github.com:jbranchaud/pokemon_deluxe.git (push) ``` ================================================ FILE: git/use-external-diff-tool-like-difftastic.md ================================================ # Use External Diff Tool Like Difftastic Assuming we already have a tool like `difft` ([difftastic](https://difftastic.wilfred.me.uk/introduction.html)) available on our machine, we can use it as a diff viewer for the various `git` commands that display a diff. This requires a manual override which involve two pieces — an inline configuration of `diff.external` specifying the binary of the external differ and the `--ext-diff` flag which tells these commands to use the external diff binary. Here is what `git show` looks like with `difft`: ```bash $ git -c diff.external=difft show --ext-diff ``` Without the `--ext-diff` flag, it will fallback to the default differ despite `diff.external` being set. See `man git-diff` and friends for the `--ext-diff` flag. See `man git-config` for `diff.external`. ================================================ FILE: git/using-commands-with-a-relative-date-format.md ================================================ # Using Commands With A Relative Date Format If you want to know what changed on a branch _since_ last week, you can more or less ask just like that: ```bash $ git log --since="1 week ago" ``` Or, what has happened since yesterday: ```bash $ git log --after="yesterday" ``` The `--since`/`--after` flags, and their counterparts `--until`/`--before`, accept a variety of date formats including _relative dates_. Relative dates can be used with other commands and even as a ref modifier. For instance, this is a way of comparing `develop` from a week ago with `develop` from two weeks ago: ```bash $ git diff develop@{"1 week ago"} develop@{"2 weeks ago"} ``` [source](https://alexpeattie.com/blog/working-with-dates-in-git) ================================================ FILE: git/verbose-commit-message.md ================================================ # Verbose Commit Message Git allows you to display a *diff* of the staged changes in the commit message buffer. This gives you the opportunity to carefully craft your commit message in a way that accurately describes the changes being committed. To display the diff when committing: ```bash $ git commit -v ``` ================================================ FILE: git/viewing-a-file-on-another-branch.md ================================================ # Viewing A File On Another Branch Sometimes you want to view a file on another branch (without switching branches). That is, you want to view the version of that file as it exists on that branch. `git show` can help. If your branch is named `my_feature` and the file you want to see is `app/models/users.rb`, then your command should look like this: ``` $ git show my_feature:app/models/users.rb ``` You can even tab-complete the filename as you type it out. See `man git-show` for more details. [source](http://stackoverflow.com/questions/7856416/view-a-file-in-a-different-git-branch-without-changing-branches) ================================================ FILE: git/what-changed.md ================================================ # What Changed? If you want to know what has changed at each commit in your Git history, then just ask `git whatchanged`. ```bash $ git whatchanged commit ddc929c03f5d629af6e725b690f1a4d2804bc2e5 Author: jbranchaud Date: Sun Feb 12 14:04:12 2017 -0600 Add the source to the latest til :100644 100644 f6e7638... 2b192e1... M elixir/compute-md5-digest-of-a-string.md commit 65ecb9f01876bb1a7c2530c0df888f45f5a11cbb Author: jbranchaud Date: Sat Feb 11 18:34:25 2017 -0600 Add Compute md5 Digest Of A String as an Elixir til :100644 100644 5af3ca2... 7e4794f... M README.md :000000 100644 0000000... f6e7638... A elixir/compute-md5-digest-of-a-string.md ... ``` This is an old command that is mostly equivalent to `git-log`. In fact, the man page for `git-whatchanged` says: > New users are encouraged to use git-log(1) instead. The difference is that `git-whatchanged` shows you the changed files in their raw format which can be useful if you know what you are looking for. See `man git-whatchanged` for more details. ================================================ FILE: git/what-is-the-current-branch.md ================================================ # What Is The Current Branch? This question can be answered with one of git's plumbing commands, `rev-parse`. ``` $ git rev-parse --abbrev-ref HEAD ``` The `--abbrev-ref` flag tells `git-rev-parse` to give us the short name for `HEAD` instead of the SHA. [source](http://stackoverflow.com/a/12142066/535590) ================================================ FILE: git/whitespace-warnings.md ================================================ # Whitespace Warnings You can configure git to warn you about whitespace issues in a file when you diff it. This is handled by the `core.whitespace` configuration. Add the following to your `.gitconfig` file: ``` [core] whitespace = warn ``` By default, git will warn you of trailing whitespace at the end of a line as well as blank lines at the end of a file. ================================================ FILE: github/access-your-github-profile-photo.md ================================================ # Access Your GitHub Profile Photo Let's say I have my [GitHub profile](https://github.com/jbranchaud) pulled up in the browser. ``` https://github.com/jbranchaud ``` If I then add `.png` to the end of that in the URL bar: ``` https://github.com/jbranchaud.png ``` I'll be redirected to the URL where the full image file lives. In my case: ``` https://avatars.githubusercontent.com/u/694063?v=4 ``` You can pull up yours `https://github.com/.png` to access your profile image. [source](https://dev.to/10xlearner/how-to-get-the-profile-picture-of-a-github-account-1d82) ================================================ FILE: github/open-a-pr-to-an-unforked-repo.md ================================================ # Open A PR To An Unforked Repo Sometimes I will clone a repo to explore the source code or to look into a potential bug. If my curiosity takes me far enough to make some changes, then I jump through the hoops of creating a fork, reconfiguring branches, pushing to my fork, and then opening the branch as a PR against the original repo. The `gh` CLI allows me to avoid all that hoop-jumping. Directly from the cloned repo I can use `gh` to create a new PR. It will prompt me to creat a fork. If I accept, it will seamlessly create it and then open a PR from my fork to the original. ```bash $ gh pr create ``` This allows me to create the PR with a few prompts from the CLI. If you prefer, you can include the `--web` flag to open the PR creation screen directly in the browser. ================================================ FILE: github/target-another-repo-when-creating-a-pr.md ================================================ # Target Another Repo When Creating A PR I have a [`dotfiles` repo](https://github.com/jbranchaud/dotfiles) that I forked from [`dkarter/dotfiles`](https://github.com/dkarter/dotfiles). I'm adding a bunch of my own customizations on a `main` branch while continually pulling in and merging upstream changes. The primary remote according to `gh` is `jbranchaud/dotfiles`. 98% of the time that is what I want. However, I occasionally want to share some changes upstream via a PR. Running `gh pr create` as is will create a PR against my fork. To override this on a one-off basis, I can use the `--repo` flag. ```bash $ gh pr create --repo dkarter/dotfiles ``` This will create a PR against `dkarter:master` from my branch (e.g. [`jbranchaud:jb/fix-hardcoded-paths`](https://github.com/dkarter/dotfiles/pull/373)). See `man gh-pr-create` for more details. ================================================ FILE: github/tell-gh-what-the-default-repo-is.md ================================================ # Tell gh What The Default Repo Is I recently forked [dkarter/dotfiles](https://github.com/dkarter/dotfiles) as a way of bootstrapping a robust dotfile config for a new machine that I could start making customizations to. I'm maintaining a `my-dotfiles` branch and keep things in sync with the original upstream repo. When trying to go to *my* fork of the repo ([jbranchaud/dotfiles](https://github.com/jbranchaud/dotfiles)) in the web with the `gh` CLI tool, I ran into a weird issue. It was instead opening up to `dkarter/dotfiles`. `gh` was under the wrong impression which repo should be considered the default. To clarify things for `gh`, there is a command to set the default repo. ```bash $ gh repo set-default jbranchaud/dotfiles ✓ Set jbranchaud/dotfiles as the default repository for the current directory ``` Now when I run `gh repo view --web`, it opens the browser to my fork of the dotfiles. But where does this setting live? Opening this repo's `.git/config` file I can see a section for the `origin` remote that includes a new line for `gh-resolved`. This being set to `base` tells `gh` that this remote is the one to treat as the default repo. ``` [remote "origin"] url = git@github.com:jbranchaud/dotfiles.git fetch = +refs/heads/*:refs/remotes/origin/* gh-resolved = base ``` See `gh repo set-default --help` for more details. ================================================ FILE: github-actions/cache-playwright-dependencies-across-workflows.md ================================================ # Cache Playwright Dependencies Across Workflows With the help of `actions/cache@v3`, I can cache the dependency install and setup involved with using Playwright in GitHub Actions. That setup, in my experience, typically takes ~45s. When it is already cached, it is able to skip that step entirely greatly reducing the overall run time of the script. First, I need to define a cache (`playwright-cache`). Second, I need to only install the Playwright dependencies when that cache isn't available (`cache-hit != 'true'`). Here is a striped down workflow demonstrating that. ```yaml name: Playwright Script on: workflow_dispatch: jobs: Cached-Playwright-Script: runs-on: ubuntu-latest steps: - name: Check out repository code uses: actions/checkout@v3 - uses: actions/cache@v3 id: playwright-cache with: path: | ~/.cache/ms-playwright key: ${{ runner.os }}-playwright-${{ hashFiles('**/package-lock.json') }} - name: Install dependencies run: npm ci - name: Install playwright deps run: npx playwright install --with-deps chromium if: steps.playwright-cache.outputs.cache-hit != 'true' - run: node playwright-script.js ``` If I add the caching step and the cache-conditional `playwright install` steps to another workflow in this project, the cache will be available to both of them. That means they both benefit from the savings of that work having already been cached. [source](https://justin.poehnelt.com/posts/caching-playwright-in-github-actions/) ================================================ FILE: github-actions/capture-an-output-value-for-use-in-a-later-step.md ================================================ # Capture An Output Value For Use In A Later Step GitHub Actions has a workflow command called `set-output`. This can be used to capture the output from a shell command in step. That output value can then be used in a later step. A useful example of this is reading the version of a tool from a dot-file to tell a later step what version of that tool to install. Here's the `.tool-versions` file included in my repository: ``` postgres 13.1 ruby 3.0.0 nodejs 15.4.0 ``` Assuming I've already [checked out my repo](https://github.com/actions/checkout), I can find and read the `nodejs` version from my `.tool-versions` file with a step that uses `set-output`. ```yaml - name: Read Node.js version to install from `.tool-versions` id: nodejs run: >- echo "::set-output name=NODE_VERSION::$( cat .tool-versions | grep nodejs | sed 's/nodejs \(.*\)$/\1/' )" ``` `echo` runs the command in the string which sets `NODE_VERSION` as an output value to what ends up being `15.4.0`. This output value can be referenced in a later step. ```yaml - name: Install required Node.js version uses: actions/setup-node@v1 with: node-version: "${{ steps.nodejs.outputs.NODE_VERSION }}" ``` `steps` has a reference to the `nodejs` step (note the `id` above) which then has `outputs` like the `NODE_VERSION`. [source](https://docs.github.com/en/actions/reference/workflow-commands-for-github-actions#using-workflow-commands-to-access-toolkit-functions) ================================================ FILE: github-actions/disable-a-workflow-with-the-gh-cli.md ================================================ # Disable A Workflow With The gh CLI You may want to temporarily disable a GitHub Actions workflow without deleting the file for the workflow. In my case, this is handy because I want to keep a scheduled workflow around as a point of reference, but I don't want it running all the time. This can be done with [the `workflow` subcommand of the `gh` CLI](https://docs.github.com/en/actions/using-workflows/disabling-and-enabling-a-workflow?tool=cli). First, list the workflows for your current repo so that you can figure out the workflow ID that you want to disable. ```bash $ gh workflow list GitHub Actions Demo active 60018591 Playwright Demo active 60142509 Scheduled Actions Demo active 60028624 ``` Now, copy the ID of the workflow you want to disable. In my case, it is `60028624`. Then, run the `disable` command for that workflow ID: ```bash $ gh workflow disable 60028624 ✓ Disabled Scheduled Actions Demo ``` That workflow is now disabled and it is no longer going to show up in the default listing of workflows. If you want to see it in the list though, you can include the `--all` flag. ```bash $ gh workflow list --all GitHub Actions Demo active 60018591 Playwright Demo active 60142509 Scheduled Actions Demo disabled_manually 60028624 ``` ================================================ FILE: github-actions/reference-an-encrypted-secret-in-an-action.md ================================================ # Reference An Encrypted Secret In An Action CI environments like GitHub Actions want to help you keep your secrets secret. They allow you to store an encrypted version of a secret that can be access in an action. First, you need to add an encrypted secret to your repository. Navigate to your repository's _Settings_ page and then to the _Secrets_ tab. You can then add the secret with the _New repository secret_ button. Once the secret is added, you can reference it in your actions. Let's say you added your repository secret with the name `MY_API_KEY`. And then let's say that your project needs to be built with that API key available in the environment as `SECRET_API_KEY`. You can reference it from `secrets` in the `env` section. ```yaml - name: Build the JS bundle run: yarn build env: SECRET_API_KEY: ${{ secrets.MY_API_KEY }} ``` Though this API key is stored on GitHub's servers as an encrypted value, it will be decrypted when this workflow step is run. [source](https://docs.github.com/en/actions/reference/encrypted-secrets#using-encrypted-secrets-in-a-workflow) ================================================ FILE: github-actions/trigger-a-workflow-via-an-api-call.md ================================================ # Trigger A Workflow Via An API Call We can set up a GitHub Actions workflow to run when triggered by an API call. This is done with the [`workflow_dispatch` event](https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#workflow_dispatch). First, we add `workflow_dispatch` to our workflow as a triggering event: ```yaml on: workflow_dispatch: ``` Second, we create a fine-grained personal GitHub access token that has permissions for dispatching to GitHub Actions. More details on that in the [GitHub docs](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens#creating-a-fine-grained-personal-access-token). Then, we can use `cURL` or some other tool for issuing an HTTP POST request to [the workflow dispatch API endpoint](https://docs.github.com/en/rest/actions/workflows?apiVersion=2022-11-28#create-a-workflow-dispatch-event). The `cURL` request will look something like this: ```bash curl -L \ -X POST \ -H "Accept: application/vnd.github+json" \ -H "Authorization: Bearer "\ -H "X-GitHub-Api-Version: 2022-11-28" \ https://api.github.com/repos/OWNER/REPO/actions/workflows/WORKFLOW_ID/dispatches \ -d '{"ref":"topic-branch","inputs":{"name":"Mona the Octocat","home":"San Francisco, CA"}}' ``` Note: we need to alter that URL with the `OWNER` and `REPO` that the workflow lives in as well as the `WORKFLOW_ID` which can be the name of the workflow file (e.g. `my-dispatchable-workflow.yml`). This event also means that we can manually trigger the workflow from the GitHub Actions UI for that workflow. ================================================ FILE: github-actions/use-labels-to-block-pr-merge.md ================================================ # Use Labels To Block PR Merge Let's say our GitHub project has custom tags for both `no merge` and `wip` (_work in progress_). Whenever either of those labels has been applied to a PR, we want there to be a failed check so as to block the merge. This is useful to ensure automated tools (as well as someone not looking closely enough) don't merge a PR that isn't _ready to go_. This can be achieved with a basic GitHub Actions workflow that requires no 3rd-party actions. We can add the following as `.github/workflows/block-labeled-prs.yml` in our project. ```yaml name: Block Labeled PR Merges on: pull_request: types: [labeled, unlabeled, opened, edited, synchronize] jobs: prevent-merge: if: ${{ contains(github.event.*.labels.*.name, 'no merge') || contains(github.event.*.labels.*.name, 'wip') }} name: Prevent Merging runs-on: ubuntu-latest steps: - name: Check for label run: | echo "Pull request label prevents merging." echo "Labels: ${{ join(github.event.*.labels.*.name, ', ') }}" echo "Remove the blocking label(s) to skip this check." exit 1 ``` This workflow is run when a pull request is opened, when it is edited or synchronized, and when a label change is made. The job `prevent-merge` sees if any of the label names match `no merge` or `wip`. If so, we echo out some details in the ubuntu container and then `exit 1` to fail the check. Shoutout to [Jesse Squire's implementation](https://www.jessesquires.com/blog/2021/08/24/useful-label-based-github-actions-workflows/#updated-21-march-2022) which I've heavily borrowed from here. ================================================ FILE: go/access-go-docs-offline.md ================================================ # Access Go Docs Offline The Go language has a wonderfully comprehensive standard library. There is documentation for all of it. You can access that documentation anytime if you have an internet connection via [https://golang.org/doc/](golang.org/doc/). If you are without an internet connection, you're still in luck. Go has a built-in feature for serving the documentation locally _offline_. Just run the following command: ``` $ godoc -http=:6060 ``` and then visit `localhost:6060`. Note: if you do not already have `godoc` installed, you can install it with: ```bash $ go install golang.org/x/tools/cmd/godoc@latest ``` [source](http://www.andybritcliffe.com/post/44610795381/offline-go-lang-documentation) ================================================ FILE: go/add-a-method-to-a-struct.md ================================================ # Add A Method To A Struct Given a `struct` in Go, we can attach a method to that struct. Put another way, we can define a method whose receiver is that struct. Then with an instance of that struct, we can call the method. Let's say we are modeling a turtle that can move around a 2D grid. A turtle has a heading (the direction it is headed) and a location (its current X,Y coordinate). ```go type Heading string const ( UP Heading = "UP" RIGHT Heading = "RIGHT" DOWN Heading = "DOWN" LEFT Heading = "LEFT" ) type Turtle struct { Direction Heading X int Y int } ``` We can then add a method like so by specifying the receiver as the first part of the declaration: ```go func (turtle *Turtle) TurnRight() { switch turtle.Direction { case UP: turtle.Direction = RIGHT case RIGHT: turtle.Direction = DOWN case DOWN: turtle.Direction = LEFT case LEFT: turtle.Direction = UP } } ``` The receiver is a pointer to a `Turtle`. The method is called `TurnRight`. There are no parameters or return values. Here are a sequence of calls to demonstrate how it works: ```go func main() { turtle := Turtle{UP, 5, 5} fmt.Println("Turtle Direction:", turtle.Direction) //=> Turtle Direction: UP turtle.TurnRight() fmt.Println("Turtle Direction:", turtle.Direction) //=> Turtle Direction: RIGHT turtle.TurnRight() fmt.Println("Turtle Direction:", turtle.Direction) //=> Turtle Direction: DOWN } ``` [source](https://go.dev/tour/methods/1) ================================================ FILE: go/basic-delve-debugging-session.md ================================================ # Basic Delve Debugging Session When using [delve](https://github.com/go-delve/delve) to debug a Go program, these are the series of things I usually find myself doing. First, I start running the program with `dlv` including any arguments after a `--` (in my case, the `solve` subcommand and a filename). ```bash $ dlv debug . -- solve samples/001.txt ``` `dlv` starts up and is ready to run my program from the beginning. I'll need to set a couple breakpoints before continuing. I do this with the `break` command, specifying the filename and line number. ``` (dlv) break main.go:528 Breakpoint 1 set at 0x10c1a5bea for main.traversePuzzleIterative() ./main.go:528 (dlv) break main.go:599 Breakpoint 2 set at 0x10c1a6dcc for main.traversePuzzleIterative() ./main.go:599 ``` Now I can continue which will run the program until hitting a breakpoint. ``` (dlv) continue > [Breakpoint 2] main.traversePuzzleIterative() ./main.go:599 (hits goroutine(1):1 total:1) (PC: 0x10c1a6dcc) 594: } 595: } 596: 597: topStackFrame := stack[len(stack)-1] 598: // if the current stack frame has more values, try the next => 599: if len(topStackFrame.PossibleValues) > 0 { 600: nextValue := topStackFrame.PossibleValues[0] 601: topStackFrame.PossibleValues = topStackFrame.PossibleValues[1:] 602: topStackFrame.CurrValue = nextValue 603: 604: // Undo the last placement and make a new one ``` I can see the context around the line we've stopped on. From here I can dig into the current state of the program by looking at local variables (`locals`) or printing out a specific value (`print someVar`). I can continue to step through the program line by line with `next` or eventually run `continue` to proceed to the next breakpoint. ``` (dlv) locals diagnostics = main.Diagnostics {BacktrackCount: 0, NodeVisitCount: 1, ValidityCheckCount: 2,...+2 more} stack = []main.StackData len: 1, cap: 1, [...] emptyCellPositions = [][]int len: 3, cap: 4, [...] emptyCellIndex = 1 status = "Invalid" topStackFrame = main.StackData {RowIndex: 1, ColumnIndex: 7, PossibleValues: []int len: 8, cap: 8, [...],...+1 more} (dlv) print topStackFrame main.StackData { RowIndex: 1, ColumnIndex: 7, PossibleValues: []int len: 8, cap: 8, [2,3,4,5,6,7,8,9], CurrValue: 1,} (dlv) next > main.traversePuzzleIterative() ./main.go:600 (PC: 0x10c1a6dea) ``` ================================================ FILE: go/build-for-a-specific-os-and-architecture.md ================================================ # Build For A Specific OS and Architecture Go programs can run anywhere, but you've got to create builds specific to each operating system and architecture. This can be done when building by specifying the `GOOS` and `GOARCH` environment variables. For example, if you'd like to build a 32-bit Linux distribution: ```bash GOOS=linux GOARCH=386 go build -o linux_386_build ``` The `GOOS` value specifies the operating system as Linux and the `GOARCH` value of `386` specifies a 32-bit architecture. The plethora of `GOOS` and `GOARCH` options can be found [here](https://golang.org/doc/install/source#environment). ================================================ FILE: go/check-if-cobra-flag-was-set.md ================================================ # Check If Cobra Flag Was Set When using [Cobra](https://github.com/spf13/cobra) to define a CLI, we can specify a flag for a command like so: ```go var Seed int64 myCmd.PersistentFlags().Int64VarP(&Seed, "seed", "", -1, "set a seed") ``` This `--seed` flag has a _default_ of `-1`. If the flag isn't specified, then when we access that flag's value, we'll get `-1`. But how do we differentiate between the _default_ `-1` and someone passing `-1` to the `--seed` flag when running the program? In the command definition, we can look at the flags and see, by name, if specific ones were changed by user input rather than being the defaults. ```go myCommand := &cobra.Command{ // coommand setup ... Run: func(cmd *cobra.Command, args []string) { if cmd.Flags().Changed("seed") { seed, err := cmd.Flags().GetInt64("seed") if err != nil { fmt.Println("Seed flag is missing from `cmdFlags()`") os.Exit(1) } fmt.Printf("Seed was set to %d\n", seed) } else { fmt.Println("Seed was not set") } } } ``` If we don't want to rely on the default and instead want to specify some other behavior when the flag is not manually set by the user, we can detect that scenario like this. ================================================ FILE: go/combine-two-slices.md ================================================ # Combine Two Slices The `append` function can be used to create a new slice with the contents of the given slice and one or more items added to the end. We can add one or more items like so: ```go s1 := []int{1, 2, 3, 4} s2 := append(s1, 5) s3 := append(s2, 6, 7, 8) fmt.Println(s1) //=> [1 2 3 4] fmt.Println(s2) //=> [1 2 3 4 5] fmt.Println(s3) //=> [1 2 3 4 5 6 7 8] ``` But what if we have a second slice instead of individual items? We could import `slices` and use its `Concat` function. Or we can stick with `append` and unpack that slice as a series of arguments into the second part of `append` using `slice...`. ```go s4 := append(s2, s1...) fmt.Println(s4) //=> [1 2 3 4 5 1 2 3 4] ``` Here is the full example: ```go package main import ( "fmt" ) func main() { s1 := []int{1, 2, 3, 4} s2 := append(s1, 5) s3 := append(s2, 6, 7, 8) fmt.Println(s1) fmt.Println(s2) fmt.Println(s3) s4 := append(s2, s1...) fmt.Println(s4) } ``` [source](https://pkg.go.dev/builtin#append) ================================================ FILE: go/configure-max-string-print-length-for-delve.md ================================================ # Configure Max String Print Length For Delve During a [Delve](https://github.com/go-delve/delve) debugging session, we can print out the value of a given variable with the `print` command. Similarly, we can see the values of all local variables with the `locals` command. Whenever Delve is printing out strings and slices, it will truncate what it displays to 64 characters (or items) by default. ```go (dlv) print diagnostics.Solutions[0] "295743861\n431865972\n876192543\n387459216\n612387495\n549216738\n7635...+25 more" ``` This can be overridden by [changing the `config` of `max-string-len`](https://github.com/derekparker/delve/blob/237c5026f40e38d2dd6f62a7362de7b25b00c1c7/Documentation/cli/expr.md?plain=1#L59) to something longer. In my case here, all I need are about 90 characters to display my full string, so run `config max-string-len 90` from the `dlv` session. ```go (dlv) config max-string-len 90 (dlv) print diagnostics.Solutions[0] "295743861\n431865972\n876192543\n387459216\n612387495\n549216738\n763524189\n928671354\n154938627" ``` Now I can see the entire string instead of the truncated version. [source](https://stackoverflow.com/a/52416264/535590) ================================================ FILE: go/connect-to-a-sqlite-database.md ================================================ # Connect To A SQLite Database Using the `database/sql` module and the `github.com/mattn/go-sqlite3` package, we can connect to a SQLite database and run some queries. In my case, I have a SQLite connection string exported to my environment, so I can access that with `os.Getenv`. It's a local SQLite file, `./test.db`. Calling `sql.Open`, I'm able to connect with a SQLite3 driver to the database at that connection string. The `setupDatabase` function returns that database connection pointer. Things like `Exec` and `QueryRow` can be called on `db`. I also need to make sure I close the connection to the database with a `defer`. Here is a full example of connecting to a local SQLite database and inserting a record: ```go package main import ( "database/sql" "fmt" "os" _ "github.com/mattn/go-sqlite3" ) func setupDatabase() *sql.DB { databaseString := os.Getenv("GOOSE_DBSTRING") if len(databaseString) == 0 { fmt.Println("Error retrieving `GOOSE_DBSTRING` from env") os.Exit(1) } db, err := sql.Open("sqlite3", databaseString) if err != nil { fmt.Printf("Error opening database: %v\n", err) os.Exit(1) } return db } func main() { db := setupDatabase() defer db.Close() sql := `insert into users (name) values (?);` db.Exec(sql, "Josh") } ``` ================================================ FILE: go/create-a-slice-from-an-array.md ================================================ # Create A Slice From An Array Slices in Go are a flexible abstraction over arrays. We can create a slice from an array with the `[n:m]` _slicing_ syntax. We specify the left and right (exclusive) bounds of the array that we want to create the slice relative to. We can exclude the lower bound which translates to the `0` index of the array. We can exclude the left bound which translates to the end of the array. We can even exclude both ends of the _slicing_ syntax which means creating a slice of the entire array. Here is an example of each of those: ```go package main import "fmt" func main() { arr := [...]string{ "taco", "burrito", "torta", "enchilada", "quesadilla", "pozole", } firstTwo := arr[:2] lastTwo := arr[len(arr)-2:] all := arr[:] fmt.Println("First two:", firstTwo) // First two: [taco burrito] fmt.Println("Last two:", lastTwo) // Last two: [quesadilla pozole] fmt.Println("All:", all) // All: [taco burrito torta enchilada quesadilla pozole } ``` [source](https://go.dev/blog/slices-intro#slices) ================================================ FILE: go/detect-if-stdin-comes-from-a-redirect.md ================================================ # Detect If Stdin Comes From A Redirect Reading lines of input from `stdin` is flexible. And we may need our program to behave differently depending on where that input is coming from. For instance, if data is redirected or piped to our program, we scan and process it directly. Otherwise, we need to prompt the user to enter in specific info and go from there. We can detect whether [`os.Stdin`](https://pkg.go.dev/os#pkg-variables) is being piped to, redirected to, or whether we should prompt the user by looking at the file mode descriptor of [`os.Stdin.Stat()`](https://pkg.go.dev/os#File.Stat). ```go package main import ( "bufio" "fmt" "os" ) func main() { file, err := os.Stdin.Stat() if err != nil { fmt.Printf("Error checking stdin: %v\n", err) os.Exit(1) } fromTerminal := (file.Mode() & os.ModeCharDevice) != 0 fromAPipe := (file.Mode() & os.ModeNamedPipe) != 0 if fromTerminal { fmt.Println("This is Char Device mode, let's prompt user for input") termScanner := bufio.NewScanner(os.Stdin) for termScanner.Scan() { fmt.Printf("- %s\n", termScanner.Text()) break; } } else if fromAPipe { fmt.Println("This is Named Pipe mode, contents piped in") pipeScanner := bufio.NewScanner(os.Stdin) for pipeScanner.Scan() { fmt.Printf("- %s\n", pipeScanner.Text()) } } else { fmt.Println("This means the input was redirected") redirectScanner := bufio.NewScanner(os.Stdin) for redirectScanner.Scan() { fmt.Printf("- %s\n", redirectScanner.Text()) } } } ``` If `os.ModeCharDevice` then we are connected to a character device, like the terminal. We can see if input is being piped in by checking against `os.ModeNamedPipe`. Otherwise, there are a variety of file modes and I'm willing to assume we're dealing with a regular file redirect at that point. ================================================ FILE: go/deterministically-seed-a-random-number-generator.md ================================================ # Deterministically Seed A Random Number Generator If you need a random number in Go, you can always reach for the various functions in the `rand` package. ```go package main import ( "fmt" "math/rand" ) func main() { for range 5 { roll := rand.Intn(6) + 1 fmt.Printf("- %d\n", roll) } } ``` Each time I run that, I get a random set of values. Often in programming, we want some control over the randomness. We want to _seed_ the randomness so that it is deterministic. We want random, but the kind of random where we know how we got there. ```go package main import ( "fmt" "math/rand" ) func main() { seed := int64(123) src := rand.NewSource(seed) rng := rand.New(src) for range 5 { roll := rng.Intn(6) + 1 fmt.Printf("- %d\n", roll) } } ``` In this second snippet, we create a `Source` with a specific seed value that we can use with a custom `Rand` struct. We can then deterministically get random numbers from it. ================================================ FILE: go/difference-between-slice-and-pointer-to-slice.md ================================================ # Difference Between Slice And Pointer To Slice Though a slice can be thought of and used as a flexible, variable-length array-like data structure, it is important to understand that it is also a special kind of pointer to an underlying array. This matters when we a function receives a slice versus a pointer to a slice as an argument, depending on what it is doing with that slice. If the function is access or updating elements in the slice, there is no difference. There is no meaningful difference between these two functions and we might as well use the former. ```go func replaceAtIndex(slice []string, index int, value string) { slice[index] = value } func replaceAtIndexPtr(slice *[]string, index int, value string) { (*slice)[index] = value } ``` On the other hand, if the receiving function needs to append to or replace the slice, then we need to pass a pointer to the slice. A direct slice argument will result in only the function-local copy getting replaced. ```go package main import ( "fmt" ) func main() { s1 := []int{8, 6, 7, 9} s2 := []int{8, 6, 7, 9} addItem(s1, 11) fmt.Printf("s1: %v\n", s1) //=> s1: [8 6 7 9] addItemPtr(&s2, 11) fmt.Printf("s2: %v\n", s2) //=> s2: [8 6 7 9 11] } func addItem(slice []int, value int) { slice = append(slice, value) } func addItemPtr(slice *[]int, value int) { (*slice) = append(*slice, value) } ``` [source](https://go.dev/tour/moretypes/8) ================================================ FILE: go/do-something-n-times.md ================================================ # Do Something N Times With Go 1.23 there is a new for-range syntax that makes looping a bit easier and more compact. Instead of needing to set up our 3-part for-loop syntax, we can say we want to do something `N` times with `for range N`. ```go for range n { // do something } ``` Let's look at an actual, runnable example: ```go package main import "fmt" import "math/rand" import "time" func main() { rand.Seed(time.Now().UnixNano()) food := []string{"taco", "burrito", "torta", "enchilada", "tostada"} for range 5 { randomIndex := rand.Intn(len(food)) fmt.Println(food[randomIndex]) } } ``` The output is random and might look something like this: ```bash $ go run loop.go taco burrito tostada taco enchilada ``` I appreciate this syntax addition because it feels very akin to Ruby's `#times` method: ```ruby 5.times do # do something end ``` [source](https://eli.thegreenplace.net/2024/ranging-over-functions-in-go-123/) ================================================ FILE: go/find-executables-installed-by-go.md ================================================ # Find Executables Installed By Go When you install an executable using `go install`, it puts that executable in the `bin` directory designated by the `GOBIN` environment variable. If that env var isn't set, then it falls back to one of `$GOPATH/bin` or `$HOME/go/bin`. When I run `go help install`, it tells me as much: ``` Executables are installed in the directory named by the GOBIN environment variable, which defaults to $GOPATH/bin or $HOME/go/bin if the GOPATH environment variable is not set. ``` So, if I am to install something like [`tern`](https://github.com/jackc/tern), ```bash $ go install github.com/jackc/tern/v2@latest ``` it is going to place that binary in `~/go/bin` for me. ```bash $ which tern /Users/jbranchaud/go/bin/tern ``` ================================================ FILE: go/format-date-and-time-with-time-constants.md ================================================ # Format Date And Time With Time Constants The Go [`time` package](https://pkg.go.dev/time) has a [`Format` function](https://pkg.go.dev/time#Time.Format) for displaying the parts of a date and time in standard and custom ways. It works a bit different than you might be used to from other languages. Rather than using `strftime` identifiers like in this string `"%B %d, %Y"`, there is a canonical date that is used as a reference point. That canonical date is from Janary 2nd, 2006. That was a Monday. It was at 5 seconds after 3:04PM. The Unix format of it looks like `"Mon Jan _2 15:04:05 MST 2006"`. ``` package main import ( "fmt" "time" ) func main() { // This specific time pulled from `time.Format` docs t, _ := time.Parse(time.UnixDate, "Wed Feb 25 11:06:39 PST 2015") // Reference date and time: // "Mon Jan _2 15:04:05 MST 2006" strf1 := t.Format("|2006|02|01|03:04:05|Day: Mon|") fmt.Println("strf1:", strf1) // strf1: |2015|25|02|11:06:39|Day: Wed| strf2 := t.Format(time.DateTime) strf3 := t.Format(time.RubyDate) strf4 := t.Format(time.Kitchen) fmt.Println("DateTime:", strf2) // DateTime: 2015-02-25 11:06:39 fmt.Println("RubyDate:", strf3) // RubyDate: Wed Feb 25 11:06:39 +0000 2015 fmt.Println("Kitchen:", strf4) // Kitchen: 11:06AM } ``` Though there are a [variety of useful formatting constants](https://pkg.go.dev/time#pkg-constants) already available like `DateTime`, `RubyDate`, `Kitchen`, etc., we can also define our own formatting string by using the reference values for each part of a date and time. If you want to reference the year, whether as `YYYY` or `YY`, it is always going to be a form of `2006`, so `2006` or `06` respectively. Even though the above time variable is in February, our format strings will always need to use one of `Jan`, `January`, `01` or `1`. ================================================ FILE: go/not-so-random.md ================================================ # Not So Random Go's `rand` package makes it easy to generate all sorts of pseudo-random numbers. So if you write a program like so: ```go package main import "fmt" import "math/rand" func main() { stuff := []string{ "one", "two", "three", "four", } fmt.Println(stuff[rand.Intn(len(stuff))]) } ``` and then run it, you will get output like: ``` three ``` and any subsequent runs of the program will continue to produce `three`. This is because the default seed for global functions in `math/rand` is [specified](https://golang.org/pkg/math/rand/#Seed) as `1`. If you want your program to be a little less predictable, you will want to seed it yourself, perhaps with the current time, instead of `1`. Try adding the following to the beginning of the `main` function: ```go rand.Seed(time.Now().UTC().UnixNano()) ``` You'll also want to import the `time` package. Things should *appear* to be a bit more random now. source: [Jake Worth](https://twitter.com/jwworth) and [Stackoverflow](http://stackoverflow.com/questions/12321133/golang-random-number-generator-how-to-seed-properly) ================================================ FILE: go/parse-a-string-into-individual-fields.md ================================================ # Parse A String Into Individual Fields Let's say you're reading in data from a file or otherwise dealing with an arbitrary string of data. If that string has a series of values separated by whitespace, you can parse it into individual fields with [`strings.Fields`](https://pkg.go.dev/strings#Fields). ```go import ( "fmt" "strings" ) func main() { data := "3 5 2 6 7 1 9" fields := strings.Fields(data) fmt.Printf("Fields: %v", fields) // [3 5 2 6 7 1 9] } ``` Here is another example where we can see that `strings.Fields` deals with multiple whitespace and surrounding whitespace: ```go import ( "fmt" "strings" ) func main() { data := " go java c++ rust " fields := strings.Fields(data) fmt.Printf("%v", fields) // [go java c++ rust] } ``` ================================================ FILE: go/parse-flags-from-cli-arguments.md ================================================ # Parse Flags From CLI Arguments Though we can grab the arguments to a Go program from `os.Args`, it requires some manual parsing. With the built-in `flag` package, we can declare specific flags our program accepts, by type. When we parse them, they will be separated out from the rest of the positional arguments. Here is an example of the program that accepts a boolean `debug` flag. This will work with either `-debug` or `--debug`. ```go package main import ( "flag" "fmt" "os" ) func main() { var debug bool flag.BoolVar(&debug, "debug", false, "turns on debug mode, extra logging") flag.Parse() positionalArgs := flag.Args() if len(positionalArgs) < 1 { fmt.Println("Please specify which part to run: 1 or 2") os.Exit(1) } if debug { fmt.Println("We are in debug mode...") fmt.Println("Received the following argument:", positionalArgs[0]) } // ... } ``` We can run the program in debug mode like so: ```bash $ go run . --debug 123 We are in debug mode... Received the following argument: 123 ``` We can also take advantage of the `help` flag that we get for free: ```bash $ go run . --help Usage of /var/folders/62/lx9pcjbs1zbd83zg6twwym2r0000gn/T/go-build3212087168/b001/exe/test: -debug turns on debug mode, extra logging ``` Note: any recognized flags need to come before any of the position arguments. The `debug` flag won't be picked up if we run the program like this: ```bash $ go run . 123 --debug ``` [source](https://pkg.go.dev/flag) ================================================ FILE: go/pass-a-struct-to-a-function.md ================================================ # Pass A Struct To A Function Go operates as _pass-by-value_ which means that when we pass a struct to a function, the receiving function gets a copy of the struct. Two things worth noticing about that are 1) an extra memory allocation happens when calling the function and 2) altering the struct does not affect the original in the calling context. On the other hand, we can have a function that takes a pointer to a struct. When we call that function, we have a reference to the memory location of the struct instead of a copy of the struct. That means no additional allocation and modifications to the dereferenced struct are modifications to the original in the calling context. Here is an example that demonstrates both of these. Notice the printed output that is included in comments at the end which shows memory locations and contents of the struct at various points. ```go package main import "fmt" type Order struct { Item string Quantity int DineIn bool } func main() { order := Order{Item: "taco", Quantity: 3, DineIn: true} fmt.Println("Order:", order) fmt.Printf("main - Loc: %p\n", &order) doubledOrder := doubleOrder(order) fmt.Println("Double Order:", doubledOrder) fmt.Println("Original Order:", order) doubleOrderPtr(&order) fmt.Println("Double Order Ptr:", order) } func doubleOrder(order Order) Order { fmt.Printf("doubleOrder - Loc: %p\n", &order) order.Quantity *= 2 return order } func doubleOrderPtr(order *Order) { fmt.Printf("doubleOrderPtr - Loc: %p\n", order) (*order).Quantity *= 2 } // Order: {taco 3 true} // main - Loc: 0xc0000b4000 // doubleOrder - Loc: 0xc0000b4040 // Double Order: {taco 6 true} // Original Order: {taco 3 true} // doubleOrderPtr - Loc: 0xc0000b4000 // Double Order Ptr: {taco 6 true} ``` ================================================ FILE: go/produce-the-zero-value-of-a-generic-type.md ================================================ # Produce The Zero Value For A Generic Type While writing a _pop_ function that would work with slices of a generic type, I ran into the issue of needing to produce a zero value of type `T` when returning early for an empty slice. The way to arbitrarily get the zero value of a generic in Go is with `*new(T)`. I was able to use this in my `Pop` function like so: ```go func Pop[T any](slice []T) (T, error) { if len(slice) == 0 { return *new(T), fmt.Errorf("cannot pop an empty slice") } lastItem := slice[len(slice)-1] slice = slice[:len(slice)-1] return lastItem, nil } ``` If this is happening in multiple functions and we want a more self-documenting approach, we can pull it out into a function `zero`: ```go func zero[T any]() T { return *new(T) } ``` ================================================ FILE: go/redirect-file-to-stdin-during-delve-debug.md ================================================ # Redirect File To Stdin During Delve Debug I have a go program that accepts input from stdin. The way I've been running the program as I develop it is to redirect the output of some sample files to the program. ```bash $ go run . < sample/001.txt ``` When I then go to debug this program with [Delve](https://github.com/go-delve/delve), I'd still like to be able to redirect a file into the program to reproduce the exact behavior I'm seeing. The following won't work: ```bash $ dlv debug . < samples/001.txt Stdin is not a terminal, use '-r' to specify redirects for the target process or --allow-non-terminal-interactive=true if you really want to specify a redirect for Delve ``` Fortunately, `dlv` sees what I'm trying to do and makes a recommendation. The `-r` flag can be used to specify redirects for the target process. The [`dlv` redirect docs](https://github.com/go-delve/delve/blob/master/Documentation/usage/dlv_redirect.md) explain that `-r` can be passed a `source:destination`. The `source` is `stdin` by default, but can also be `stdout` and `stderr`. I can redirect my file into the debugging session of my program like so: ```bash $ dlv debug . -r stdin:samples/001.txt ``` Or even more succinctly: ```bash $ dlv debug . -r samples/001.txt ``` ================================================ FILE: go/replace-the-current-process-with-an-external-command.md ================================================ # Replace The Current Process With An External Command Go's `syscall.Exec` function can be used to execute an external program. Instead of forking a child process though, it runs the external command in place of the current process. You need to give the function three pieces of information: the location of the binary, the pieces of the command to be executed, and relevant environment. Here is a simple example. ```go package main import "fmt" import "os" import "syscall" func main() { // get the system's environment variables environment := os.Environ() // get a slice of the pieces of the command command := []string{"tmux", "new-session", "-s", "burrito"} err := syscall.Exec("/usr/local/bin/tmux", command, environment) if err != nil { fmt.Printf("%v", err) } } ``` When this program is executed, it will replace itself with a new tmux session named *burrito*. ================================================ FILE: go/sleep-for-a-duration.md ================================================ # Sleep For A Duration Many languages allow you to sleep for a certain number of milliseconds. In those languages, you can give `500` or `1000` to the sleep function to sleep for half a second and a second respectively. In Go, the duration of a call to [`time.Sleep`](https://golang.org/pkg/time/#Sleep) is in nanoseconds. Fortunately, there are constants that make it easy to sleep in terms of milliseconds. For example, you can sleep for a half a second (500 milliseconds) like so: ```go package main import ( "time" ) func main() { time.Sleep(500 * time.Millisecond) } ``` Other available time constants are `Nanosecond`, `Microsecond`, `Second`, `Minute`, `Hour`. ================================================ FILE: go/sort-slice-in-ascending-or-descending-order.md ================================================ # Sort Slice In Ascending Or Descending Order The [`slices.Sort`](https://pkg.go.dev/slices#Sort) function defaults to sorting a slice in ascending order. If we want to control the sort order, we have to do a little more work. We can reach for the [`slices.SortFunc`](https://pkg.go.dev/slices#SortFunc) function. This allows us to define a sort function and in that function we can control whether the sort order is ascending or descending. Here I've defined `SortItems` which takes a list of items constrained by the [`cmp.Ordered`](https://pkg.go.dev/cmp#Ordered) interface (so things like `int`, `string`, `uint64`, etc.). It takes a direction (`ASC` or `DESC`) as a second argument. It does the directional sort based on that second argument. ```go import ( "cmp" "fmt" "slices" ) type Direction int const ( ASC Direction = iota DESC ) func SortItems[T cmp.Ordered](items []T, dir Direction) { slices.SortFunc(items, func(i, j T) int { if dir == ASC { return cmp.Compare(i, j) } else if dir == DESC { return cmp.Compare(j, i) } else { panic(fmt.Sprintf("Unrecognized sort direction: %d", dir)) } }) } // items := []int{3,2,8,1} // SortItems(items, ASC) // // items => [1,2,3,8] // SortItems(items, DESC) // // items => [8,3,2,1] ``` Because `slices.SortFunc` expects a negative value, zero, or positive value to determine the sort order, we use [`cmp.Compare`](https://pkg.go.dev/cmp#Compare) which returns those kinds of values. For ascending, we compare `i` to `j`. For descending, we swap them, comparing `j` to `i` to get the reverse sort order. ================================================ FILE: go/upgrading-from-an-older-version-on-mac.md ================================================ # Upgrading From An Older Version On Mac To upgrade from an older version on Mac, there are a couple manual steps that you need to take. For starters, download the latest installer for Mac from [Go Lang Downloads](https://golang.org/dl/). While this is downloading, you'll need to delete the older version of Go that is installed on your machine. First, remove the existing Go installation directory: ```bash $ sudo rm -rf /usr/local/go ``` Second, clean up the Go `bin` directory from your `PATH` environment variable: ```bash $ sudo rm /etc/paths.d/go ``` Now, you can double click on the downloaded installer dmg and follow the prompt instructions. When its all said and done, check `go version` from the command line to see that you are now working with the latest. ================================================ FILE: go/write-a-custom-scan-function-for-file-io.md ================================================ # Write A Custom Scan Function For File IO By default a [`bufio.Scanner`](https://pkg.go.dev/bufio#Scanner) will scan input line-by-line. In other words, splitting on newlines such that each iteration will emit everything up to the next newline character. We can write our own `SplitFunc` and override the default one by calling `scanner.Split` with it. Our custom scan function needs to match the type signature of [`SplitFunc`](https://pkg.go.dev/bufio#SplitFunc). Here is a custom one that emits each individual character but omits the newlines. ```go func ScanChar(data []byte, atEOF bool) (int, []byte, error) { if atEOF || len(data) == 0 { return 0, nil, nil } start := 0 for start < len(data) { if !utf8.FullRune(data[start:]) { return 0, nil, nil } r, size := utf8.DecodeRune(data[start:]) if r == utf8.RuneError { return 0, nil, fmt.Errorf("invalid UTF-8 encoding") } if r != '\n' { return start + size, data[start:start+size], nil } // found a \n, advance the start position start += size } return start, nil, nil } ``` We can then use thi `ScanChar` function with a `bufio.Scanner` like so: ```go func ReadFileByCharacter(file io.Reader) { scanner := bufio.NewScanner(file) // override default SplitFunc scanner.Split(scanChar) for scanner.Scan() { char := scanner.Text() fmt.Printf("- %s\n", char) } } ``` ================================================ FILE: groq/grab-multiple-values-from-a-reference.md ================================================ # Grab Multiple Values From A Reference Let's say we have an `author` with some attributes including a reference to a `person` which contains more data about the person. Here is one way to write a query to access that data. ```groq *[_type == 'author' && slug.current == 'donna-tartt']{ website, 'firstName': person->firstName, 'lastName': person->lastName, 'age': person->age } ``` Here is another way to write this query that doesn't do three separate accesses on the `person` reference. ```groq *[_type == 'author' && slug.current == 'donna-tartt']{ website, person-> { 'firstName': firstName, 'lastName': lastName, 'age': age } } ``` This isn't quite right though because it leaves the three reference values nested under `person`. We can get back to the original shape of our query by flattening the `person` object using familiar looking spread syntax (`...`). ```groq *[_type == 'author' && slug.current == 'donna-tartt']{ website, ...person-> { 'firstName': firstName, 'lastName': lastName, 'age': age } } ``` ================================================ FILE: groq/grab-values-from-an-array-of-references.md ================================================ # Grab Values From An Array Of References Let's say we have a `post` object in our schema. A `post` can have an array of references to `tags` telling you what topics the post covers. Each `tag` has a slug and we want to get the `string` value for each slug. Let's say we are interested in the post with `_id` of `123`. Here is how we can achieve that with a `groq` query: ```groq *[ _type == 'post' && _id == 123 ]{ 'tags': tags[]->slug.current }.tags => ["javascript", "react-js"] ``` If the schema was such that each `post` just had a single tag reference, then you could write the chain of references as `tag->slug.current`. Because it is an array of references, we need the `[]` to declare that we want each value. The `->` operator follows the reference. Otherwise we'd just have access to the `_ref` and `_type` values. The final `.tags` unnests the `tags` value we gathered into an object. Then the result is just the array of slug values. ================================================ FILE: groq/include-attributes-when-conditional-check-passes.md ================================================ # Include Attributes When Conditional Check Passes The graph-like nature of Sanity means that you'll often be querying for data that spans a variety of document types. For instance, your front-end could be requesting content in the shape of a blog post, video, and podcast. Though there are some similarities, each of these document types will have some unique attributes. When using GROQ to query for data that spans different types of documents, you can use [a conditional query syntax](https://www.sanity.io/docs/query-cheat-sheet#64a36d80be73) to include type-specific attributes. ```groq *[_type == 'blog' || _type == 'video' || _type == 'podcast'][]{ title, 'slug': slug.current, _type == 'blog' => { body, read_time }, _type == 'video' => { description, mp4_url }, _type == 'podcast' => { description, mp3_url } } ``` Notice that there are some attributes that are common across each type (i.e. `title` and `slug`). Each type then has attributes unique to its document type (i.e. `blog` has `body` and `read_time`). This conditional query syntax allows us to both define flexible schemas in Sanity and then query against that flexible schema. [source](https://www.youtube.com/watch?v=dCGPNkcTseQ) ================================================ FILE: groq/include-type-of-operation-in-webhook-response.md ================================================ # Include Type Of Operation In Webhook Response When setting up a webhook in Sanity Studio, you can specify what types of operations you want to trigger the webhook: `create`, `update`, and `delete`. If you include all three for a webhook, then you'll probably want to know in your webhook handler which type of operation you're receiving. Webhooks are run in _Delta Mode_ which means that a collection of GROQ functions are available in the _filter_ and _projection_ blocks. These functions include [`delta::operation`](https://www.sanity.io/docs/groq-functions#a64594a50318). Here is what a projection using `delta::operation` might look like: ```groq { _id, firstName, lastName, email, 'operation': delta::operation() } ``` This function can resolve to either `create`, `update`, or `delete`. ================================================ FILE: heroku/check-ruby-version-for-production-app.md ================================================ # Check Ruby Version For Production App While deploying a fresh Rails app to Heroku recently, I ran into an issue. The `it` block argument wasn't working despite being on Ruby 4.0. Or so I thought. Running the following command reported the Ruby version of that Heroku server instance: ```bash ❯ heroku run -- ruby --version Running ruby --version on ⬢ my-app... up, run.3090 ruby 3.3.9 (2025-07-24 revision f5c772fc7c) [x86_64-linux] ``` I was on `3.3.9` which must have been the fallback default at the time. Though I had set the Ruby version in my `.ruby-version` file, I had neglected to specify it in the `Gemfile` as well. Once I added it to the `Gemfile` and redeployed, my Heroku server instance was running the expected version of Ruby. ```bash ❯ heroku run -- ruby --version Running ruby --version on ⬢ my-app... up, run.5353 ruby 4.0.0 (2025-12-25 revision 553f1675f3) +PRISM [x86_64-linux] ``` Note: because [I have set `HEROKU_ORGANIZATION` and `HEROKU_APP`](set-default-team-and-app-for-project.md) in my environment (`.envrc`) for the local copy of the app, I don't need to specify those when running the `heroku run` command above. See `heroku run --help` for more details. ================================================ FILE: heroku/connect-to-a-database-by-color.md ================================================ # Connect To A Database By Color All of your PostgreSQL databases in Heroku are given attachment names that use a random color. This might be _pink_, _brown_, _cobalt_, etc. And the attachment names then look like `HEROKU_POSTGRESQL_PINK`, `HEROKU_POSTGRESQL_BROWN`, `HEROKU_POSTGRESQL_COBALT`, etc. We can connect to a Heroku-managed PostgreSQL instance from the command-line like so: ```bash $ heroku pg:psql --app my-app ``` This is going to connect to the _default_ database which is the one with the `DATABASE_URL` attachment. There are lots of instances where we may have other databases besides the primary (e.g. let's say we have a read replica follower). If we want to connect to that one, we can do so by _color_. If that database's attachment is `HEROKU_POSTGRESQL_IVORY`, then we'd connect to it like so: ```bash $ heroku pg:psql ivory --app my-app ``` [source](https://devcenter.heroku.com/articles/managing-heroku-postgres-using-cli#pg-psql) ================================================ FILE: heroku/deploy-a-review-app-to-a-different-stack.md ================================================ # Deploy A Review App To A Different Stack Heroku has different stacks for deploying apps. As newer versions of Linux distros (such as Ubuntu) come out and software packages need patching and updating, Heroku releases new stacks. And deprecates older ones. Before upgrading a production app to a new stack, you should test it out first. The recommended way to test this out is with a Review App. If you need to bump the Ruby version or make any other changes, do that first. Then add or update your `app.json` file. This is a Heroku-specific file that tells Heroku what stack to use when creating a new app or review app. ```json { "stack": "heroku-18" } ``` Let's say my app is currently on the Heroku-16 stack. I can set the `stack` to be `heroku-18` in `app.json`. Then I can push up a branch with all these changes and turn it into a PR. From the Heroku dashboard, I can click the Heroku Button that builds a Review App from the PR. Once it it finishes building and deploying, Heroku will give me a custom URL for visiting the app so that I can manually evaluate it. [source](https://devcenter.heroku.com/articles/upgrading-to-the-latest-stack#testing-an-app-on-a-new-stack) ================================================ FILE: heroku/diagnose-problems-in-a-heroku-postgres-database.md ================================================ # Diagnose Problems In A Heroku Postgres Database Heroku keeps track of all kinds of diagnostics on the performance of your app's database. From long running queries and transactions to index cache hit rates to unused indexes to tables with bloat. Running the `pg:diagnose` command for your Heroku app will surface all of these details in the terminal. ```bash $ heroku pg:diagnose -a APP_NAME ``` The report will start with the biggest problem areas which it color codes in red. If your app is experiencing degraded performance, the list of red items would be a good place to start investigating. The report will then list less urgent problem areas. Those will be color coded yellow. Though Heroku has deemed these less serious, you may still want to deal with these. All the other areas of diagnose will fall into the _green_ bucket. Meaning Heroku doesn't see any issues in those areas. ================================================ FILE: heroku/open-dashboard-for-specific-add-on.md ================================================ # Open Dashboard For Specific Add-On The number of times I've needed to check the papertrail logs for my Heroku-hosted Rails app is a lot. I open a browser tab, go through several layers of navigation to get to my app's dashboard, and then click the papertrail link under _Add-ons_. There is a much quicker way using the Heroku CLI. ```bash $ heroku addons:open papertrail -a my-app-name Opening https://addons-sso.heroku.com/apps/abc123/addons/efg456... ``` It sends you to an add-ons SSO link in the browser which authenticates you and drops you into the dashboard for that specific add-on. You just need to specify the add-on name and the app name. ================================================ FILE: heroku/run-sql-against-remote-postgres-database.md ================================================ # Run SQL Against Remote Postgres Database You can access a remote Heroku PostgreSQL database through a `psql` session using the following command: ```bash heroku pg:psql --app my-app ``` That opens an interactive psql session. If instead you'd like to run a single SQL script against that remote database, you can redirect that script to the connection. ```bash heroku pg:psql --app my-app < query.sql ``` The results of running that SQL will be written to stdout. You can take this a step further by redirecting the output into another file so that you can review and search the results at your convenience, rather than only having them appear in your terminal. ```bash heroku pg:psql --app my-app < query.sql > results.out ``` ================================================ FILE: heroku/set-and-show-heroku-env-variables.md ================================================ # Set And Show Heroku Env Variables The `heroku` CLI includes the `config` command which will show all the env variables for the current app. ```bash $ heroku config === my-app Config Vars ``` At this point I have none set. I can set one with the `config:set` command and a key-value parameter. ```bash $ heroku config:set PUBLIC_NEXT_URL=http://localhost:1235 Setting PUBLIC_NEXT_URL and restarting ⬢ my-app... done, v3 PUBLIC_NEXT_URL: http://localhost:1235 ``` I can see the value that this specific variable is set to by calling it with `config:get`. ```bash $ heroku config:get PUBLIC_NEXT_URL http://localhost:1235 ``` [source](https://devcenter.heroku.com/articles/config-vars#using-the-heroku-cli) ================================================ FILE: heroku/specify-default-team-and-app-for-project.md ================================================ # Specify Default Team And App For Project Typically when you run commands with the Heroku CLI you'll need to specify the name of the app on Heroku you're targeting with the `--app` flag. However, to first see the names of the apps you may want to run `heroku apps` (or `heroku list`). That will list the apps for your default team. If you need to see apps for a different team (i.e. organization), you'll need to specify that team either with the `--team` flag or by setting that as an environment variable. Here I do the latter in an `.envrc` file: ``` # Heroku export HEROKU_ORGANIZATION=visualmode ``` Once that is set and the environment reloaded, running `heroku apps` will show the apps specific to that team on Heroku. Similarly, if you want to set a default app for your project so that you don't have to always specify the `--app` flag, you can update your `.envrc` accordingly. ``` # Heroku export HEROKU_ORGANIZATION=visualmode export HEROKU_APP=my-app ``` I had a hard time finding official documentation for this which is why I'm writing this up here. I've manually verified this works with my own team and app. ================================================ FILE: heroku/ssh-into-heroku-server-hosting-app.md ================================================ # SSH Into Heroku Server Hosting App Heroku hosts and deploys your app on a remote server. One of Heroku's primary offerings is that it abstracts away the details of that server. You generally don't need to think about it. Instead you can use the Heroku CLI to interact with it indirectly. There will inevitably be a time when you need to access that server directly. Heroku gives you a way to do this with their CLI through [`ps:exec`](https://devcenter.heroku.com/articles/exec). ```bash $ heroku ps:exec --app my-app ``` This will connect you to an SSH session with the server that the app is running on. ================================================ FILE: html/adding-alt-text-to-an-image.md ================================================ # Adding Alt Text To An Image Images on their own are not accessible to anyone using a screen reader. As the people putting content on the web, we can make images more accessible by providing _alternative_ text. This is such a standard that linters not only flag `` tags that are missing the `alt` attribute, they also admonish you for unhelpful description text like "image." You can appease the linter and make your content accessible with some descriptive text: ```html a graph with lines trending up ``` When appropriate, you can also choose to include the `alt` attribute with a blank value. > If an image is purely decorative, then we add alt="" to let screen readers > know that it’s not important. But if an image is informative, then we need to > be supplying a text alternative that describes the picture for anyone who’s > using a screen reader or isn’t able to see the image. Part of accessibility is not putting a bunch of noise in front of your users. If the image isn't part of the content, use `alt=""`. [source](https://24ways.org/2019/twelve-days-of-front-end-testing/) ================================================ FILE: html/allow-number-input-to-accept-decimal-values.md ================================================ # Allow Number Input To Accept Decimal Values Here is a number input element: ```html ``` This renders an empty number input box with up and down arrows which will, by default, increment or decrement the value by **1**. Of course, I can manually edit the input typing in a value like `1.25`. However, when I submit that via an HTML form, the submission will be prevented and the browser will display a validation error. > Please enter a valid value. The two nearest valid values are 1 and 2. If I want to be able to input a decimal value like this, I need to change the `step` value. It defaults to `1`, but I could change it to `2`, `10`, or in this case to `0.01`. ```html ``` Notice now that as you click the up and down arrows, the value is incremented and decremented by **0.01** at a time. If I want to maintain the step value of `1` while allowing decimal values, I can instead set the `step` value to be `any`. ```html ``` See the [MDN docs on number inputs](https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/input/number) for more details. ================================================ FILE: html/determine-which-button-submitted-the-form.md ================================================ # Determine Which Button Submitted The Form It is pretty common for a form to have a singular submit button. If the user clicks 'Submit', then the form fires a `POST` off to the server, the server can process the request, and that's it. But what about a form that has two or more buttons? For instance, imagine some kind of consent form where the user needs to either _Accept_ or _Reject_ some terms. Just like other inputs, [the ` ``` In addition to the `email` attribute, when the user submits the form, it will include a `commit` attribute that has a value of either `'accept'` or `'reject'`. Naming it `commit` is [a convention I'm borrowing from Rails's form helpers](https://guides.rubyonrails.org/v5.0/form_helpers.html#a-generic-search-form). You can name it whatever makes sense to you. ================================================ FILE: html/disable-auto-completion-for-a-form-input.md ================================================ # Disable Auto-Completion For A Form Input The browser wants to be helpful by making informed suggestions about what should be auto-filled into form inputs. We may not want this behavior. It could be a source of data entry mistakes, an annoyance, or just not something we want our users to experience. We can turn this off at an individual field level with the `autocomplete` attribute: ```html ``` Note: It is `off` and not something like `false`. [source](https://developer.mozilla.org/en-US/docs/Web/Security/Securing_your_site/Turning_off_form_autocompletion) ================================================ FILE: html/disclose-additional-details.md ================================================ # Disclose Additional Details You can add extra details to an HTML page that are only disclosed if the user chooses to disclose them. To do that, we use the `
` tag. This tag needs to have a `` tag nested within it. Anything else nested within `
` will be what is disclosed when it is toggled open. The `` is what is displayed when it is not open. Here is a `` block I recently added to [Ruby Operator Lookup](https://www.visualmode.dev/ruby-operators). ```html
What is this thing?

Ruby is an expressive, versatile, and flexible dynamic programming language. That means there are all kinds of syntax features, operators, and symbols we can encounter that might look unfamiliar and are hard to look up. Ruby Operator Lookup is a directory of all these language features.

Use the search bar to narrow down the results. Then click on a button for the operator or symbol you want to explore further.

``` On page load, the only thing we see is "What is this thing?" with a triangle symbol next to it. If we click the summary, then the entire details block (those two `

` tags) are disclosed. [source](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/details) ================================================ FILE: html/make-elements-non-interactive-with-inert.md ================================================ # Make Elements Non-Interactive With Inert The [`inert` attribute](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/inert) global attribute is a boolean that can be applied to an element or section of content in an HTML document. When it is `true`, that elements and anything nested within it will not be interactive. ```html

``` This has a couple different implications: 1. Click events are not fired on these elements. 2. These elements will not be able to gain focus. 3. These elements and content are hidden from assistive technology. This is useful for a variety of things. In particular, it is good for accessibility when a portion of the document, like a fancy animation, isn't meant to be traversed by assistive technology. ================================================ FILE: html/prevent-search-engines-from-indexing-a-page.md ================================================ # Prevent Search Engines From Indexing A Page The `robots.txt` file is commonly used to tell (well-behaved) crawlers, such as search engines, to not visit a page. If another page links to your page, it will still be indexed. To instruct search engines to not index a given page, robot meta tags need to be used. > If you want to reliably block a page from showing up in the search results, > you need to use a meta robots `noindex` tag. That means that, in order to > find the `noindex` tag, the search engine has to be able to access that page, > so don’t block it with `robots.txt`. > [source](https://yoast.com/ultimate-guide-robots-txt/) To prevent indexing, add the following meta tag to the `` section of any relevant pages. ```html ``` [source](https://developers.google.com/search/docs/advanced/crawling/block-indexing) ================================================ FILE: html/render-text-as-superscript.md ================================================ # Render Text As Superscript There is an [HTML tag](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/sup) that tells browser engines to render text as superscript -- like when you need an exponent. ```

Render this text as superscript!

``` Here is what the above would look like:

Render this text as superscript!

Check out this [codepen example](https://codepen.io/jbranchaud/pen/GzoyXe) to play around with it. ================================================ FILE: html/submit-a-form-with-a-button-outside-the-form.md ================================================ # Submit A Form With A Button Outside The Form You can tie a submit button to a form that the button doesn't live inside of. The trick is to give the form an `id` and then reference that `id` with the button's `form` property. ```html
``` With this setup, clicking the _Submit_ button will cause the form to be submitted. See the [MDN Button docs](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/button) for more details. ================================================ FILE: http/what-counts-as-cross-origin-with-cors.md ================================================ # What Counts As Cross-Origin With CORS? When it comes to HTTP, an [origin](https://developer.mozilla.org/en-US/docs/Glossary/origin) is defined by several different aspects of the URL. This is important for understanding what qualifies as _same_ and _cross_-origin when dealing with [CORS](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS) (Cross-Origin Resource Sharing). For something to be _same-origin_, it must have the same scheme (HTTP/HTTPS), the same host, and the same port. If any one of the scheme, host (including subdomains), or port is different, then it is not _same-origin_. Here are some examples of different origins: - `https://example.com` vs `http://example.com` (different scheme) - `https://example.com` vs `https://sub.example.com` (different host) - `https://example.com:3000` vs `https://example.com:5000` (different port) As long as the scheme, host, and port match, they are the same origin. The path (everything following the origin) doesn't factor into the question of same origin. ================================================ FILE: inngest/ensure-lookup-can-be-retried.md ================================================ # Ensure Lookup Can Be Retried A common thing to do in a workflow step is to look up a record. This might be a record that was created or updated around the time that the workflow was triggered. You need to be sure the record was found before proceeding. That might end up looking like this: ```typescript export default inngest.createFunction( { id: "record-user-purchase" }, { event: "app/record.purchase" }, async ({ event, step }) => { const checkoutSession = await step.run("find checkout session", async () => { const cs = provider.lookupSession(event.checkoutSessionId) return cs; }); if(!checkoutSession) { throw new Error('Checkout session not found') } } ); ``` This approach has a subtle problem which is that the error for a missing checkout session is raised _outside_ the step that sets `checkoutSession`. As inngest does a series of retries, the memorized `checkoutSession` step won't be rerun and the error will continue to be thrown. It is better to raise the error _within_ the lookup step: ```typescript export default inngest.createFunction( { id: "record-user-purchase" }, { event: "app/record.purchase" }, async ({ event, step }) => { const checkoutSession = await step.run("find checkout session", async () => { const cs = provider.lookupSession(event.checkoutSessionId) if(!cs) { throw new Error('Checkout session not found') } return cs; }); } ); ``` If the checkout session is missing on the first couple tries, the step will have a chance to retry the lookup and maybe eventually find what it is looking for. ================================================ FILE: inngest/exit-function-early-without-retries.md ================================================ # Exit Function Early Without Retries When an Inngest function fails due to an error, it will be retried up to 3 times with a scheduled back-off. That functionality is built-in. In some cases, there is no sense retrying because the failure case isn't going to change. No sense in wasting resources on retries that are going to yield the same result. In this case, we can have our code raise a `NonRetriableError`. ```javascript import { NonRetriableError } from "inngest" import {inngest} from '@/inngest/inngest.server' import {database} from '@/server/database' export default inngest.createFunction( { id: "reindex-post-for-search" }, { event: "post.updated" }, async ({ event }) => { const post = await database.findPost({ id: event.data.postId }) if(!post) { throw new NonRetriableError(`Post not found for id (${event.data.postId})`) } // handle reindexing of the post } ) ``` When inngest catches a `NonRetriableError` it knows to not schedule retries. In the context of a try/catch block where some other error has been raised, we can pass that error as a second argument to the `NonRetriableError` for additional info: ```javascript catch(err) { const message = `Post not found for id (${event.data.postId})` throw new NonRetriableError(message, { cause: err }) } ``` [source](https://www.inngest.com/docs/functions/retries) ================================================ FILE: internet/add-emoji-to-github-repository-description.md ================================================ # Add Emoji To GitHub Repository Description GitHub restricts the set of unicode characters that can appear in the description field of a repository to anything up to `0xffff`. Most emoji have unicode values above this. This means you will have limited success adding emoji via your system's emoji keyboard. Fortunately, GitHub allows you to add any recognized emoji to a repository description with its specialized emoji syntax (e.g. `:memo:`). You may have used this syntax in other parts of GitHub such as the issue tracker. If you add an emoji like this: ![](http://i.imgur.com/Tty7Cl2.png) it will show up like so: ![](http://i.imgur.com/yxRwmkW.png) Check out this [Emoji Cheat Sheet](http://www.emoji-cheat-sheet.com/) for the names of all recognized emojis. ================================================ FILE: internet/add-styled-alerts-to-github-markdown-documents.md ================================================ # Add Styled Alerts To GitHub Markdown Documents The GFM (GitHub Flavored Markdown) variant of markdown adds some nice features to our GitHub-rendered markdown documents. One such feature that has been around for a couple years, but which I only just learned about, are these styled alerts. There are five of them each with a different color and icon to help convey meaning. ``` > [!NOTE] > Useful information that users should know, even when skimming content. > [!TIP] > Helpful advice for doing things better or more easily. > [!IMPORTANT] > Key information users need to know to achieve their goal. > [!WARNING] > Urgent info that needs immediate user attention to avoid problems. > [!CAUTION] > Advises about risks or negative outcomes of certain actions. ``` I just added the following to the top of one of my project's READMEs to help me remember that it is not under active development. ``` > [!WARNING] > This repo is not under active development, you might be looking for > [til-visualmode-dev](https://github.com/jbranchaud/til-visualmode-dev). ``` Visit the GitHub docs for [Alerts](https://docs.github.com/en/get-started/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax#alerts) to see examples of how these render. [source](https://docs.github.com/en/get-started/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax#alerts) ================================================ FILE: internet/analyze-your-website-performance.md ================================================ # Analyze Your Website Performance The [PageSpeed Insights](https://pagespeed.web.dev/) tool from Google is a great way to quickly get actionable insights about where to improve your website and app's _Performance_, _Accessibility_, and _SEO_. To see how your public site or app does, grab its URL and analyze it at [PageSpeed Insights](https://pagespeed.web.dev/). It will take a minute to run on either Mobile or Desktop (make sure to check both) and then will output four headline numbers (out of 100) for each of the categories. You can then dig in to each category to see what recommendations they make for improving your score. This can also be run directly from Chrome devtools which is useful if you want to see how a locally running site is doing. You can run the analysis from the _Lighthouse_ tab of devtools. Note: if the _Performance_ score looks bad, it might be that you are running a non-optimized dev server that isn't reflective of how your site would do in production. ================================================ FILE: internet/check-your-public-ip-address.md ================================================ # Check Your Public IP Address If you visit [https://checkip.amazonaws.com/](https://checkip.amazonaws.com/), a plain text page will render that displays your current public IP address. Alternatively, you can `cURL` this endpoint from the command line: ```bash $ curl https://checkip.amazonaws.com/ ``` Either way, you'll get your IP address. Do with that what you will. ================================================ FILE: internet/digraph-unicode-characters-have-a-titlecase.md ================================================ # Digraph Unicode Characters Have a Titlecase Coming from primarily being exposed to the US American alphabet, I'm familiar with characters that I type into the computer having one of two cases. Either it is lowercase by default (`c`) or I can hit the shift key to produce the uppercase version (`C`). Unicode, which has broad support for character encoding across most languages, has a couple characters that are called _digraphs_. These are single code points, but look like they are made up of two characters. A good example of this is `dž`. And if that character were to appear in an all uppercase word, then it would display as `DŽ`. But what if it appears at the beginning of a capitalized word? That's where _titlecase_ comes into the picture -- `Dž`. From [wikipedia](https://en.wikipedia.org/wiki/D%C5%BE): > Note that when the letter is the initial of a capitalised word (like Džungla > or Džemper, or personal names like Džemal or Džamonja), the ž is not > uppercase. Only when the whole word is written in uppercase, is the Ž > capitalised. (I find it odd that wikipedia's article on this digraph code point is using separate characters instead of the digraph.) [source](https://devblogs.microsoft.com/oldnewthing/20241031-00/?p=110443) ================================================ FILE: internet/download-a-google-doc-as-specific-format.md ================================================ # Download A Google Doc As Specific Format I was recently given a public Google Doc URL and I was curious if I could download it from the command line. I didn't want to have to install special CLI though. I was hoping to use something like `curl`. A brief chat with Claude and I learned that not only can I use `curl`, but I can specify the format in the _export_ URL. ```bash $ export GOOGLE_DOC_URL="https://docs.google.com/document/d/157rMgHeBf76T9TZnUjtrUyyS2XPwG0tObr-OjYNfMaI" $ echo $GOOGLE_DOC_URL https://docs.google.com/document/d/157rMgHeBf76T9TZnUjtrUyyS2XPwG0tObr-OjYNfMaI $ curl -L "$GOOGLE_DOC_URL/export?format=pdf" -o doc.pdf % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 100 414 0 414 0 0 2763 0 --:--:-- --:--:-- --:--:-- 2895 100 16588 0 16588 0 0 56214 0 --:--:-- --:--:-- --:--:-- 167k $ ls doc.pdf doc.pdf ``` I append `/export` and then include the `?format=pdf` query param to specify that I want the document to be exported in PDF format. With the `-o` flag I can specify the name and extension of the output file. This is a handy on its own, but noticing that Google Docs supports other export formats, I thought it would be useful to go back-and-forth with Claude to sketch out a script that can do this and prompt me (with `fzf`) for the file type -- [here is the gist for `gdoc-download`](https://gist.github.com/jbranchaud/cf3d2028107a1bd8484eed7cca0fcdab). ================================================ FILE: internet/enable-keyboard-shortcuts-in-gmail.md ================================================ # Enable Keyboard Shortcuts In Gmail In these modern times of asynchronous communication and paperless receipts, a person's email inbox can get mighty full. Keeping that influx of emails at bay is a challenge. If you'd like to start unburying yourself and get on the path to inbox zero -- Gmail's keyboard shortcuts can help. Keyboard shortcuts are not enabled by default, so you'll need to turn them on in settings. Click the _gear_ icon, select _Settings_ and then scroll down under the _General_ tab until you find the _Keyboard shortcuts_ section. Select the _Keyboard shortcuts on_ radio button and then _Save changes_. You'll now have access to a variety of shortcuts, such as using the `j` and `k` keys to move up and down the list of emails in your inbox. [Find the one you want and hit `o` to open it up](https://til.hashrocket.com/posts/41grpsmqzu-gmail-o-). Keep exploring -- there is a whole world of keyboard shortcuts out there. h/t Jake Worth ================================================ FILE: internet/exclude-ai-overview-from-google-search.md ================================================ # Exclude AI Overview From Google Search At the top of most Google searches these days is a section of text that takes a moment to appear, presumably because it is being generated in the moment. This is Google's _AI Overview_. These are sometimes useful summaries of the article you are about to click on anyway. Other times the overview is no good, it takes up a bunch of screen real estate, and may even [10x the energy consumed by a regular search](https://www.reddit.com/r/technology/comments/1dsvefb/googles_ai_search_summaries_use_10x_more_energy/). If you want to exclude the _AI Overview_, tack on a `-ai` when writing out your search query. [source](https://www.yahoo.com/tech/turn-off-ai-overview-results-170014202.html) ================================================ FILE: internet/exclude-whitespace-changes-from-github-diffs.md ================================================ # Exclude Whitespace Changes From GitHub Diffs If you run a tidy ship and use plugins like [vim-spacejam](https://github.com/rondale-sc/vim-spacejam), then whitespace changes cluttering up your git diffs probably isn't much of an issue. However, if you are working with other people or messier code bases, then you may not be so lucky. If there are a bunch of whitespace changes in a commit, then that is going to make the diff view of a commit on GitHub annoying, and perhaps hard, to read. You can cut to the chase by excluding whitespace changes from GitHub's diff view by adding `w=1` to the diff URL. Check out [this view of the diff](https://github.com/jbranchaud/dotfiles/commit/fad58dfda91e61972b3c28e7e967bb631140e71e) and then [this view of the diff that excludes whitespace](https://github.com/jbranchaud/dotfiles/commit/fad58dfda91e61972b3c28e7e967bb631140e71e?w=1). [source](https://twitter.com/ablwr/status/789141645098938368) ================================================ FILE: internet/figure-out-your-public-ip-address.md ================================================ # Figure Out Your Public IP Address Want to know what your public IP address is? Visit [ifconfig.io](http://ifconfig.io/) to get a bunch of information about your connection including your public IP address. ================================================ FILE: internet/focus-the-url-bar.md ================================================ # Focus The URL Bar There are a lot of things you can do in the browser without having to reach for the mouse. Bringing the URL bar into focus is one of those things. Hit `Cmd+L` in any modern browser (I've tried Chrome, Firefox, and Safari) and the URL bar will be brought into focus. From there, you can quickly change the URL of the current tab and your fingers never left the keyboard. h/t Jake Worth ================================================ FILE: internet/get-random-images-from-unsplash.md ================================================ # Get Random Images From Unsplash _The below doesn't work on GitHub. It seems that GitHub caches and serves its own version of the image. This will work anywhere where you aren't caching images._ Here is an image from [unsplash.com](https://unsplash.com). ![random images](http://source.unsplash.com/random/1200x600) I don't know what image I am showing you though. That's because the URL being sourced for the above image is `http://source.unsplash.com/random/1200x600`. This tells unsplash to randomly serve us a `1200x600` image. Try refreshing the page and you'll see that it is different each time. Cool! This is a bit of a novelty, but could be useful on occasion. The [Gatsby](https://www.gatsbyjs.org/) docs use it to great effect in a tutorial so as to not get caught up with the details of serving a specific image. ================================================ FILE: internet/grab-the-rss-feed-for-a-substack-blog.md ================================================ # Grab The RSS Feed For A Substack Blog I've been attempting to put more energy into finding and reading blog posts via an RSS feed reader. This as opposed to scrolling and scrolling and hoping that the algorithm turns up an interesting article or two. A lot of people who have been blogging for a while have a handy RSS feed link prominently displayed on their site. We love to see it! There are a few people whose writing I really enjoy that distribute their words via Substack. I couldn't find a prominent or not prominent RSS feed link anywhere on someone's Substack. What I did learn, after some searching, is that you can tack `/feed` onto the end of someone's Substack URL and that will give you the XML feed. For example: ``` Substack blog landing page URL: https://registerspill.thorstenball.com Substack blog RSS feed URL: https://registerspill.thorstenball.com/feed ``` Grab that feed URL and paste it into your feed reader and you should start seeing their stuff show up. ================================================ FILE: internet/hide-overflowing-text-for-google-sheets-column.md ================================================ # Hide Overflowing Text For Google Sheets Column I imported a big CSV into a new Google Sheets document. This included a "Description" column with many of the descriptions varying between 50 and 80 characters. The bottom line is that the description column was flowing over the top of the columns next to it. Instead of expanding the width of that column as far as the largest description, I wanted to hide the _overflow_. The way to do this in Google Sheets is to highlight the entire column by clicking on the column grouping. Then under the _Format_ menu item is a _Wrapping_ submenu. The _Clip_ option is what I was looking for because it clips the text that gets shown at the edge of the column. ================================================ FILE: internet/search-tweets-by-author.md ================================================ # Search Tweets By Author Twitter has some advanced search features that allow you to do more than just search by keyword. The `from:` syntax is the way that you can restrict your search results to a specific twitter user. ![twitter from search](http://i.imgur.com/yWi9JB7.png) By using `"pair programming" from:hashrocket`, I am able to find all of the tweets by `@hashrocket` that use the exact phrase `pair programming`. ================================================ FILE: internet/show-all-pivotal-stories-with-blockers.md ================================================ # Show All Pivotal Stories With Blockers Within the past year [Pivotal Tracker](https://www.pivotaltracker.com) added a feature that allows you to mark stories with _blockers_. These are visual indicators with a description that are used to show a particular story is blocked, that is, it cannot be completed until something else is taken care of. In order to maintain the health of the project, it is good to triage these blocked stories from time to time. The best way to identify all of the blocked stories is to filter them into their own column. Enter `is:blocked` into the search bar to show all of the blocked stories. ================================================ FILE: internet/verify-site-ownership-with-dns-record.md ================================================ # Verify Site Ownership With DNS Record To run your site through Google Search Console and get detailed reports, you need to verify that you own the site. There are several manual ways of doing this that involve sticking a value unique to your URL in a file or header tag. There is a better way though. By adding a TXT DNS record wherever you domain's DNS is managed, you can prove to Google that you own the domain. That verification applies to all paths and subdomains of that domain. Some providers like Cloudflare have a mostly-automated process for this that Google can hook into as long as you grant permission via OAuth. You can also manually create the TXT record if necessary. Either way, it will look something like: ```bash $ dig -t TXT visualmode.dev ;; ANSWER SECTION: visualmode.dev. 377 IN TXT "google-site-verification=MBZ2S2fhnh2gHRxFniRrYW-O6mdyimJDRFj-f vblwtk" ``` More details are provided in the [Google Search Console docs](https://support.google.com/webmasters/answer/9008080?hl=en#domain_name_verification). ================================================ FILE: java/ensure-resources-always-get-closed.md ================================================ # Ensure Resources Always Get Closed Java has a construct known as _try-with-resource_ that allows us to always ensure opened resources get closed. This is safer than similar cleanup in the `finally` block which could still leave a memory leak if an error occurs in that block. To use the _try-with-resource_ construct, instantiate your opened resource in parentheses with the `try`. ```java try (BufferedReader reader = new BufferedReader(new FileReader(filename))) { // ... } ``` The resource will be automatically closed when the try/catch block completes. Here is a full example: ```java import java.io.BufferedReader; import java.io.FileReader; import java.io.IOException; public class FileReaderExample { public static void main(String[] args) { String fileName = "example.txt"; try (BufferedReader reader = new BufferedReader(new FileReader(fileName))) { String line; int lineCount = 0; while ((line = reader.readLine()) != null && lineCount < 5) { System.out.println(line); lineCount++; } } catch (IOException e) { System.out.println("An error occurred while reading the file: " + e.getMessage()); } } } ``` You can even specify multiple resources in one `try`. The above does that, but this will make it more obvious: ```java try (FileReader fr = new FileReader(filename); BufferedReader br = new BufferedReader(fr)) { // ... } ``` [source](https://docs.oracle.com/javase/tutorial/essential/exceptions/tryResourceClose.html) ================================================ FILE: java/install-java-on-mac-with-brew.md ================================================ # Install Java On Mac With Brew If you don't already have Java installed on your Mac, you can install it with homebrew. ```bash $ brew install java ``` This will take a bit to run and when all is complete, you'll go to run something like a version check and see this: ```bash $ java -version The operation couldn’t be completed. Unable to locate a Java Runtime. Please visit http://www.java.com for information on installing Java. ``` This is because [OpenJDK](https://openjdk.org/) the open-source implementation of the Java Development Kit (Java platform) does not get fully set up by homebrew. You'll need to symlink `openjdk` and the exact command with correct paths can be found from running the following: ```bash $ brew info openjdk ... For the system Java wrappers to find this JDK, symlink it with sudo ln -sfn /usr/local/opt/openjdk/libexec/openjdk.jdk /Library/Java/JavaVirtualMachines/openjdk.jdk ... ``` The paths may look different for you, so copy the exact command and run that. Once the symlink is set, check the version again. ```bash $ java -version openjdk version "23" 2024-09-17 OpenJDK Runtime Environment Homebrew (build 23) OpenJDK 64-Bit Server VM Homebrew (build 23, mixed mode, sharing) ``` [source](https://stackoverflow.com/a/65601197/535590) ================================================ FILE: java/run-a-hello-world-program-in-eclipse.md ================================================ # Run A Hello World Program In Eclipse First, you'll need to create a new Java Project if you don't already have one to work in. From there, you can add a new _Class_ to the `src` folder of that project. I'll call mine `Greeting.java` and the only thing it will contain is a `main` method. ```java public class Greeting { public static void main(String[] args) { String name = args.length > 0 ? args[0] : "World"; System.out.println("Hello, " + name + "!"); } } ``` This method tries to read a name from the arguments given to the program at time of execution. If one wasn't provided the ternary falls back to `"World"` as the default name. It then prints the greeting to stdout. To run this program, we can either select _Run_ from the _Run_ menu (which will result in `Hello, World!`) or we can select _Run Configurations..._ from the same menu and add a custom name to _Program Arguments_ under the _Arguments_ tab. ================================================ FILE: javascript/accessing-arguments-to-a-function.md ================================================ # Accessing Arguments To A Function The `arguments` object is available within any JavaScript function. It is an array-like object with all of the arguments to the function. Even if not all of the arguments are referenced in the function signature, they can still be accessed via the `arguments` object. ```javascript function argTest(one) { console.log(one); console.log(arguments); console.log(arguments[1]); } argTest(1); // 1 // [1] // undefined argTest(1, 'two', true); // 1 // [1,'two',true] // 'two' ``` See the [Arguments object docs](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/arguments) on MDN for more details. h/t Dorian Karter ================================================ FILE: javascript/add-item-to-an-array-of-references-in-sanity.md ================================================ # Add Item To An Array Of References In Sanity Let's say we have an existing record in our Sanity dataset. The schema for that record allows for an array of references to another type of record. As part of programmatically importing some data, we need to tie some records together by adding to that array of references. We've already set up our [Sanity client](https://www.sanity.io/docs/js-client) (via the JavaScript SDK). We have an `_id` for the record we want to patch. We have a `resourceId` for the resource that we want to _reference_ in the array. Here is how we perform that `patch`: ```javascript await sanityClient .patch(_id) .setIfMissing({resources: []}) .append('resources', [{_type: 'reference', _ref: resourceId}]) .commit({autoGenerateArrayKeys: true}) ``` 1. We give it the `_id` of the record we want to `patch`. 2. We set our array of `resources` to an empty array (`[]`) if it hasn't already been set. 3. We `append` to the `resources` array with an array containing a single item, a reference to our resource. 4. We `commit` the changes with the directive that Sanity should auto-generate the `_key` value for any new array items. ================================================ FILE: javascript/basic-date-formatting-without-a-library.md ================================================ # Basic Date Formatting Without A Library JavaScript, on modern browsers, has an Internationalization API that, among other things, provides some date formatting utilities. You can just start using it, no need to import some massive date formatting library. Here is a `Date` object: ```javascript > const now = new Date(); > now Tue Nov 19 2019 16:23:43 GMT-0600 (Central Standard Time) ``` The default formatting with this API is a good start: ```javascript > Intl.DateTimeFormat('en-US').format(now) "11/19/2019" ``` There are also a number of options for more advanced formatting. Here is the `dateStyle` option with the four possible option values: ```javascript > Intl.DateTimeFormat('en-US', { dateStyle: "full" }).format(now) "Tuesday, November 19, 2019" > Intl.DateTimeFormat('en-US', { dateStyle: "long" }).format(now) "November 19, 2019" > Intl.DateTimeFormat('en-US', { dateStyle: "medium" }).format(now) "Nov 19, 2019" > Intl.DateTimeFormat('en-US', { dateStyle: "short" }).format(now) "11/19/19" ``` There is a lot more to this API including localization and custom formatting. Check out the [docs](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DateTimeFormat) for those details. ================================================ FILE: javascript/character-codes-from-keyboard-listeners.md ================================================ # Character Codes from Keyboard Listeners If I create the following keyboard event listeners for `keydown`, `keypress`, and `keyup`: ```javascript window.addEventListener('keydown', function(e) { console.log("Keydown: " + e.charCode + ", " + e.keyCode); }); window.addEventListener('keypress', function(e) { console.log("Keypress: " + e.charCode + ", " + e.keyCode); }); window.addEventListener('keyup', function(e) { console.log("Keyup: " + e.charCode + ", " + e.keyCode); }); ``` and then I press `A`, my browser console will read the following: ``` Keydown: 0, 65 Keypress: 65, 65 Keyup: 0, 65 ``` and if I then press `a`, my browser console will read: ``` Keydown: 0, 65 Keypress: 97, 97 Keyup: 0, 65 ``` The `keypress` event seems to be the way to go. Regardless, there seems to be quite a bit of [incompatibility and lack of support](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent#Browser_compatibility) across browsers for various aspects of the keyboard events. ================================================ FILE: javascript/check-classes-on-a-dom-element.md ================================================ # Check Classes On A DOM Element You can use the [`classList` property](https://developer.mozilla.org/en-US/docs/Web/API/Element/classList) to check what classes have been assigned to a DOM element. Assuming the following DOM element: ```html ``` Once you get a handle on that element, using your preferred method (e.g. [`Document.getElementById`](https://developer.mozilla.org/en-US/docs/Web/API/Document/getElementById)), you can start inspecting the class list: ```javascript > element.classList.contains("modal") true > element.classList.contains("hidden") true > element.classList.contains("taco") false > element.classList.toString() "modal hidden" ``` ================================================ FILE: javascript/check-if-a-number-is-positive-or-negative.md ================================================ # Check If A Number Is Positive Or Negative The [`Math`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math) module has a handy function for checking if a number is _positive_ or _negative_. Or _zero_, for that matter. It is [`Math.sign`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/sign). ```javascript > Math.sign(5) 1 > Math.sign(-5) -1 > Math.sign(0) 0 ``` Any positive number will result in `1`. Any negative number will result in `-1`. If the number happens to be `0`, then `0` will be returned. This function goes real well with a switch statement. Note also that anything that isn't a number will result in `NaN`. ```javascript > Math.sign("one") NaN ``` ================================================ FILE: javascript/check-if-file-exists-before-reading-it.md ================================================ # Check If File Exists Before Reading It Let's say we are working on a script that tries to read in existing data from a JSON data file. It is possible that data file hasn't been created and populated yet. In order to account for that scenario, we need to check if the file exists. If we try to read from a non-existant file, an error will be thrown. To prevent the script from error'ing out, we can use [`fs.existsSync`](https://nodejs.org/api/fs.html#fsexistssyncpath) to check if the given file path is an existing file. If we learn that the file does exist, we can proceed with reading it. If not, we can skip the file read and react accordingly. ```javascript import fs from 'fs' const nonExistantFile = 'non-existant.json' // set default in case file does not exists let json = {} if(fs.existsSync(nonExistantFile)) { const fileData = fs.readFileSync(nonExistantFile) json = JSON.parse(fileData.toString()) } console.log('JSON: ', json) ``` [source](https://flaviocopes.com/how-to-check-if-file-exists-node/) ================================================ FILE: javascript/check-if-something-is-an-array.md ================================================ # Check If Something Is An Array The `Array` class has a function on it called [`isArray()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/isArray) which can be used to check if something is an array. ```javascript > Array.isArray('Hello, World!'); // => false > Array.isArray(['One', 2, [3]]); // => true > Array.isArray({ foo: 'bar' }); // => false > Array.isArray([]); // => true ``` The MDN docs provide an [example polyfill](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/isArray#Polyfill) if it is not natively available. ```javascript if (!Array.isArray) { Array.isArray = function(arg) { return Object.prototype.toString.call(arg) === '[object Array]'; }; } ``` ================================================ FILE: javascript/check-media-queries-from-javascript.md ================================================ # Check Media Queries From JavaScript I'm usually thinking about and [using media queries](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_media_queries/Using_media_queries) from a CSS context. I use them to control what styles are displayed for a variety of scenarios, such as at different screen widths, when a user prefers reduced motion, or when the user prefers a dark color scheme. The current value of various media queries can be checked from a JavaScript context as well. For instance, if we want to see if the user prefers a _dark_ color schema, we can look for a _match_ on that media query with [`matchMedia`](https://developer.mozilla.org/en-US/docs/Web/API/Window/matchMedia). ```javascript > window.matchMedia('(prefers-color-scheme: dark)') MediaQueryList {media: '(prefers-color-scheme: dark)', matches: true, onchange: null} > window.matchMedia('(prefers-color-scheme: dark)')['matches'] true ``` This queries for the [`prefers-color-scheme` media feature](https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-color-scheme). The [Astro.build Blog Tutorial](https://docs.astro.build/en/tutorial/6-islands/2/#add-client-side-interactivity) shows an example of using this to wire up a Light/Dark mode toggle. ================================================ FILE: javascript/check-the-password-confirmation-with-yup.md ================================================ # Check The Password Confirmation With Yup The [Yup](https://github.com/jquense/yup) library makes it easy to validate individual values in a JavaScript object. A common situation when implementing a Sign Up form is asking the user to input their password twice and then the app can make sure they match. To do this, we need the validation of our `passwordConfirmation` value to reach outside of itself to make a comparison with the `password` value. This can be done with Yup's `ref` function. ```javascript import * as Yup from 'yup'; validationSchema: Yup.object({ password: Yup.string().required('Password is required'), passwordConfirmation: Yup.string() .oneOf([Yup.ref('password'), null], 'Passwords must match') }); ``` We are able to reference the value of `password` with `ref`. We use the `oneOf` function to ensure that `passwordConfirmation` either matches `password` or if it is left blank and matches `null` then it passes the validation for the time being. The second argument to `oneOf` is a custom validation message when this validation fails. [source](https://github.com/jaredpalmer/formik/issues/90) ================================================ FILE: javascript/compare-the-equality-of-two-date-objects.md ================================================ # Compare The Equality Of Two Date Objects Equality can always feel like a bit of a moving target in JavaScript. Comparing two objects, even if visually/conceptually identical, will resolve to being not equal. To compare two `Date` objects, you first need to convert them to something we can check for equality -- numbers. This can be done with `getTime()`. ```javascript > now = Date.now() > today1 = new Date(now) > today2 = new Date(now) > today1 === today2 false > today1.getTime() === today2.getTime() true ``` With `Date` objects, you can directly use `<`, `>`, `<=`, and `>=`. [source](https://stackoverflow.com/questions/492994/compare-two-dates-with-javascript) ================================================ FILE: javascript/computed-property-names-in-es6.md ================================================ # Computed Property Names In ES6 Perhaps more often than not, property names in an object are based on something dynamic rather than a static value. In ES5, you may end up with code like the following to build an object with dynamic property names: ```javascript var dynamic1 = 'hello', dynamic2 = 'world'; var obj = {}; var propName1 = dynamic1 + dynamic2; var propName2 = 5 * 11; obj[propName1] = true; obj[propName2] = false; > obj // { 55: false, 'helloworld': true } ``` With ES6, we get _computed property names_ which allow us to do all of these inline when defining the object. We just have to wrap the code that will be evaluated to a property name in brackets: ```javascript const dynamic1 = 'hello', dynamic2 = 'world'; const obj = { [dynamic1 + dynamic2]: true, [5 * 11]: false }; > obj // { 55: false, 'helloworld': true } ``` Computed properties are [already available in many modern browsers](https://kangax.github.io/compat-table/es6/#test-object_literal_extensions_computed_properties). ================================================ FILE: javascript/conditionally-include-pairs-in-an-object.md ================================================ # Conditionally Include Pairs In An Object You can add key-value pairs to an object using the ES6 spread operator: ```javascript > { one: 1, ...{ hello: "world" } } { one: 1, hello: "world" } ``` By combining the spread operator with some boolean logic, you can conditionally add key-value pairs to an object: ```javascript > { one: 1, ...(isArriving && { hello: "world" }), } ``` Depending on the value of `isArriving`: ```javascript // isArriving === true { one: 1, hello: "world" } ``` or ```javascript // isArriving === false { one: 1 } ``` This is useful for dynamically building up some configuration object or data payload. ================================================ FILE: javascript/configure-jest-to-run-a-test-setup-file.md ================================================ # Configure Jest To Run A Test Setup File Jest can be configured to run a setup file before each test. This is useful for configuring your testing framework in a single place, rather than in each test file. This setup file can be specified in `package.json` (or `jest.config.js`). ```javascript // package.json { // ... "jest": { "setupTestFrameworkScriptFile": "src/setupTests.js" } } ``` The [`setupTestFrameworkScriptFile`](https://facebook.github.io/jest/docs/en/configuration.html#setuptestframeworkscriptfile-string) points to a test setup file at the specified location rooted at `` (the root of your project). This kind of setup is helpful for something like [Enzyme](https://github.com/airbnb/enzyme/blob/master/docs/guides/jest.md) that needs to be configured with a specific adapter for use throughout your tests. ================================================ FILE: javascript/convert-seconds-to-date-object.md ================================================ # Convert Seconds To Date Object Let's say you have an integer that represents the number of seconds since the unix epoch. This is a reasonably common way for systems to represent a date. For example, `1713350171` is an _Expiration Date_ I just got from an API. But how do we know what date that actually represents and how can we get a JavaScript `Date` object from that value? The `new Date()` constructor can produce a date object given an integer. That integer is not supposed to be seconds since the unix epoch though. See what we get here: ```javascript > new Date(1713350171) 1970-01-20T19:55:50.171Z ``` Something is off. The integer that you pass to `new Date()` needs to be the _number of milliseconds_ since the unix epoch. We can get there by multiplying our _seconds_ value by `1000`. ```javascript > new Date(1713350171 * 1000) 2024-04-17T10:36:11.000Z ``` Not only can we, as humans, read that date, but we have a `Date` object that we can use within our program. Note: if you execute `Date.now()`, the value you get is in milliseconds. ================================================ FILE: javascript/create-a-cancelable-promise-with-pcancelable.md ================================================ # Create A Cancelable Promise With PCancelable If an async task takes a really long time and we find out in the middle of its execution that we no longer want the results of that activity, it would be nice to be able to cancel that promise. There was a [proposal for cancelable promises](https://github.com/tc39/proposal-cancelable-promises), but it has since been withdrawn. There is an alternative though. The [`p-cancelable`](https://github.com/sindresorhus/p-cancelable) package provides a promise wrapper that acts as a cancelable promise. ```javascript import PCancelable from 'p-cancelable'; const fetchPromise = new PCancelable((resolve, reject, onCancel) => { setTimeout(() => { resolve({ ok: true, data: [1, 2, 3] }); }, 10000); onCancel(() => { console.log('Promise is being canceled'); }); }); fetchPromise.then(response => { console.log('Promise Resolved: ', response.data); }).catch(err => { console.log('Promise Rejected: ', err); }); fetchPromise.cancel(); ``` We can create a promise in a familiar manner. We get an additional argument -- the `onCancel` function. This is a function that will be executed if `cancel` is called before the promises has resolved or rejected. In the above example, the `fetchPromise.cancel()` line will be invoked before the `setTimeout` resolves. The promise will be canceled, causing a rejection which will push us into the `catch` handler. ================================================ FILE: javascript/create-an-array-containing-1-to-n.md ================================================ # Create An Array Containing 1 To N Some languages, such as Ruby, have a built in range constraint that makes it easy to construct an array of values from 1 to N. JavaScript is not one of those languages. Nevertheless, if you don't mind the aesthetics, you can get away with something like this: ```javascript > Array.apply(null, {length: 10}).map(Number.call, Number); => [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] ``` That gives us `0` through `9`. To get `1` through `10`, we can tweak it slightly: ```javascript > Array.apply(null, {length: 10}).map(Number.call, n => Number(n) + 1); => [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] ``` To generalize this, we can replace `10` with `N` and then just expect that `N` will be defined somewhere: ```javascript > var N = 10; => undefined > Array.apply(null, {length: N}).map(Number.call, n => Number(n) + 1); => [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] ``` [Source](http://stackoverflow.com/a/20066663/535590) ================================================ FILE: javascript/create-an-object-with-no-properties.md ================================================ # Create An Object With No Properties When you call `new Object` or even just instantiate an object with `{}`, you are creating an object that uses the `Object` prototype. This means it inherits from `Object.prototype`. You can deliberately create an object with no properties by making sure that it does not inherit `Object.prototype`. ```javascript > const propertylessObject = Object.create(null); {} > propertylessObject.__proto__ undefined ``` Unlike most objects that we encounter as we write JavaScript, this object we created with `Object.create(null)` has no properties including no `__proto__`. See [Object.create](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/create) and [Object.prototype](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/prototype) for more details. ================================================ FILE: javascript/create-bootstrapped-apps-with-yarn.md ================================================ # Create Bootstrapped Apps With Yarn The [`yarn` cli](https://yarnpkg.com/lang/en/docs/cli/) comes with a [`create`](https://yarnpkg.com/blog/2017/05/12/introducing-yarn/) command that is a convenience command for generating bootstrapped apps that follow the `create--app` convention. Want to create a React.js app using [`create-react-app`](https://github.com/facebookincubator/create-react-app), invoke the following command: ``` $ yarn create react-app my-app ``` Don't already have a particular package globally installed? `yarn create` will install it for you. For instance, the following command with install and use [`create-vue-app`](https://github.com/egoist/create-vue-app): ``` $ yarn create vue-app my-other-app ``` h/t Gabe Reis ================================================ FILE: javascript/create-future-and-past-dates-from-today.md ================================================ # Create Future And Past Dates From Today JavaScript's built-in [`Date`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date) object can be frustrating to work with at times. It does, however, lend itself nicely to some date math. You have to familiarize yourself with some of the API and then it is a matter of addition and subtraction. Here is today: ```javascript const today = new Date(); // Tue Dec 01 2020 ... ``` Let's make a copy of today and send it 30 days into the future: ```javascript const future = new Date(today); future.setDate(future.getDate() + 30); future // Thu Dec 31 2020 ... ``` Or we could jump back a few years: ```javascript const past = new Date(today); past.setFullYear(past.getFullYear() - 4); past // Thu Dec 01 2016 ... ``` [source](https://stackoverflow.com/questions/7908098/javascript-set-date-30-days-from-now/7908122#7908122) ================================================ FILE: javascript/custom-type-checking-error-messages-with-yup.md ================================================ # Custom Type Checking Error Messages With Yup In [Yup Schemas Are Validated Asynchronously](https://github.com/jbranchaud/til/blob/master/javascript/yup-schemas-are-validated-asynchronously.md), I showed how to create a simple schema that allows you to enforce that a value is a number. ```javascript const numSchema = yup.number(); ``` If we use this schema to validate something that isn't a number, Yup will provide a lengthy default message. Here is what we get if we validate against `'hey'`: > this must be a `number` type, but the final value was: `NaN` (cast from > the value `"hey"`). This value isn't necessarily suitable for displaying to a user. We can customize the type checking error message by redefining our schema with the `typeError()` function: ```javascript const numSchema = yup.number().typeError("Invalid number"); ``` ================================================ FILE: javascript/default-and-named-exports-from-the-same-module.md ================================================ # Default And Named Exports From The Same Module ES6 module syntax allows for a single default export and any number of named exports. In fact, you can have both named exports and a default export in the same module. Here is an example: ```javascript // src/animals.js export default function() { console.log('We are all animals!'); } export function cat() { console.log('Meeeow!'); } export function dog() { console.log('Rufff!'); } ``` In this case, you could import the default and named exports like so: ```javascript // src/index.js import animals, { cat, dog } from './animals.js'; animals(); // "We are all animals!" cat(); // "Meeeow!" dog(); // "Rufff!" ``` [source](http://2ality.com/2014/09/es6-modules-final.html) ================================================ FILE: javascript/define-a-custom-jest-matcher.md ================================================ # Define A Custom Jest Matcher Though [Jest's built-in matchers](https://jestjs.io/docs/en/expect) will get you pretty far in most testing scenarios, you may find yourself in a testing situation better served by a custom matcher. Custom matchers can be defined using the [`expect.extend()` function](https://jestjs.io/docs/en/expect#expectextendmatchers). Here is an example of a matcher that can check equality of two objects based solely on their `id` property. ```javascript expect.extend({ toHaveMatchingId(recieved, expected) { const pass = recieved.id === expected.id; if (pass) { return { pass: true, message: () => `expected id:${expected.id} to not match actual id:${recieved.id}` }; } else { return { pass: false, message: () => `expected id:${expected.id} to match actual id:${recieved.id}` }; } } }); ``` This defines the name of the matcher (`toHaveMatchingId`), contains some logic to figure out whether `received` and `expected` pass the matcher, and then two return conditions (`pass: true` and `pass: false`) with accompanying message-returning functions. It can then be used like any other Jest matcher: ```javascript test("compare objects", () => { expect({ id: "001" }).toHaveMatchingId({ id: "001" }); // ✅ expect({ id: "001" }).toHaveMatchingId({ id: "002" }); // ❌ expected id:002 to match actual id:001 }); ``` Check out a [live example](https://codesandbox.io/s/focused-bush-vw2s5). ================================================ FILE: javascript/destructure-with-access-to-nested-value-and-parent-value.md ================================================ # Destructure With Access To Nested Value And Parent Value A destructuring pattern that I often see (especially in React code) is to peel off a nested value in the argument declaration of a function. ```javascript const Component = ({ data: { name: displayName }}) => { return (

{displayName}

); }; ``` On its own this works quite well, but what happens when you need access to the full set of `data` as well as the nested `name` value? I often see this. ```javascript const Component = ({ data }) => { const { name: displayName } = data; return (

{displayName}

); }; ``` ES6 destructuring is flexible. You can skip the `const` line and keep everything in the argument declaration. ```javascript const Component = ({ data: { name: displayName }, data }) => { return (

{displayName}

); }; ``` You can re-reference the `data` value after the nested destructuring. ================================================ FILE: javascript/destructuring-the-rest-of-an-array.md ================================================ # Destructuring The Rest Of An Array ES6 offers some amount of pattern matching on arrays. This means you can do fun stuff like grabbing a couple values and then destructuring the rest of the array into a variable. ```javascript > const kids = ["Mike", "Will", "Dustin", "Lucas", "Eleven", "Max"]; undefined > const [first, second, ...rest] = kids; undefined > first "Mike" > second "Will" > rest ["Dustin", "Lucas", "Eleven", "Max"] ``` By using the `...` syntax with a variable name in the left-hand side of the assignment, you are able to capture an array of whatever isn't assigned to preceding variables. ================================================ FILE: javascript/easy-date-comparison-with-dayjs.md ================================================ # Easy Date Comparison With DayJS Let's say my application fetches dates from the server which come back in string form as `"YYYY-MM-DD"` and I'd like to know if those dates already passed. This can be done easily by wrapping dates in [DayJS](https://github.com/iamkun/dayjs) and using its comparison functions. ```javascript import dayjs from 'dayjs'; const today = dayjs(new Date()); const pastDate = dayjs("2018-10-22"); const futureDate = dayjs("2022-01-01"); console.log(pastDate.isBefore(today)); // => true console.log(futureDate.isBefore(today)); // => false ``` The `dayjs()` function can be used to construct DayJS date objects from Date objects and strings. These can then be compared with functions like `isBefore()` and `isAfter()`. ================================================ FILE: javascript/enable-es7-transforms-with-react-rails.md ================================================ # Enable ES7 Transforms With react-rails The [`react-rails`](https://github.com/reactjs/react-rails) gem adds JSX and ES6 transforms to the asset pipeline. By using `.js.jsx` and `.es6.jsx` extensions with relevant files, the asset pipeline will know to make the appropriate transformation when compiling application assets. ES7 transforms are not enabled by default, but can be configured. Add the following to the `config/application.js` file to allow ES7's *class properties* syntax: ```ruby config.react.jsx_transform_options = { optional: ["es7.classProperties"] } ``` h/t Mike Chau ================================================ FILE: javascript/ensure-shell-can-find-global-npm-binaries.md ================================================ # Ensure Shell Can Find Global npm Binaries When you install a package globablly either with yarn (or npm) ```bash $ yarn global add vercel ``` It places the binaries for that package in the `bin` directory of your package manager's configured path prefix. The absolute path to this `bin` directory can be found with: ```bash $ yarn global bin /Users/jbranchaud/.asdf/installs/nodejs/15.4.0/.npm/bin ``` You can ensure that everything located in that `bin` directory is on your shell's path by adding the following line to your shell's config file (e.g. `.bashrc` or `.zshrc`). ```bash export PATH="$PATH:$(yarn global bin)" ``` Once you've updated your shell config, make sure you _source_ the file or restart your shell. [source](https://stackoverflow.com/a/40333409/535590) ================================================ FILE: javascript/expand-emojis-with-the-spread-operator.md ================================================ # Expand Emojis With The Spread Operator There are a number of emojis that are not stand-alone unicode characters, but instead are [a combination of two or more other emojis](http://unicode.org/emoji/charts/full-emoji-list.html#1f468_200d_1f469_200d_1f466). The two main places this happens is with family emojis and emojis using non-simpsons skin tones. You can use JavaScript's spread operator to expand these emojis to see what their base components are. Here is a screenshot of a few that I expanded from Chrome's dev tools. ![](http://i.imgur.com/ObagJJ2.png) [source](https://twitter.com/wesbos/status/769228067780825088) ================================================ FILE: javascript/fill-an-input-with-a-ton-of-text.md ================================================ # Fill An Input With A Ton Of Text I needed to test out a form validation for an input that should render an error when the length of the context exceeds 10,000 characters. Two small tricks make this easy. First, you can target any DOM element via the Chrome dev tools by selecting it and then referencing it via the `$0` magic variable. [More details here](https://developers.google.com/web/updates/2015/05/the-currently-selected-dom-node). ```javascript > $0 ... ``` Second, you can quickly and precisely generate a very long string with the `repeat` function. ```javascript > "a".repeat(10000) "aaaaaaaaaaaaaaaaaaaaaaa..." ``` Combine these two tricks in the browser to fill the input with a ton of text: ```javascript > $0.value = "a".repeat(10000) ``` h/t Dillon Hafer ================================================ FILE: javascript/find-the-version-of-an-installed-dependency.md ================================================ # Find The Version Of An Installed Dependency I recently ran into a bug related to a specific version of a dependency. As part of tracking it down, I needed to figure out what version I had installed. The [`yarn list`](https://classic.yarnpkg.com/en/docs/cli/list) command can help with this. Without any flags, it will show a tree structure listing _every single_ dependency and sub-dependency that is installed for your project. Here is an example of what that looks like restricted to a pattern of `jest`. ```bash $ yarn list --pattern jest yarn list v1.22.10 ├─ @testing-library/jest-dom@5.14.1 ├─ @types/jest@27.0.1 ├─ @types/testing-library__jest-dom@5.9.5 │ ├─ @jest/types@26.6.2 │ ├─ @types/jest@26.0.23 │ ├─ jest-diff@26.6.2 │ └─ jest-get-type@26.3.0 ... ``` I can look through this list and find the dependency and version of interest. It's still a lot of results to comb through, so what I like to do instead is pipe it to [`fzf`](https://github.com/junegunn/fzf). ```bash $ yarn list | fzf ``` Then I can interactively narrow down the results with the power of FZF's fuzzy finding functionality. ================================================ FILE: javascript/find-where-yarn-is-installing-binaries.md ================================================ # Find Where Yarn Is Installing Binaries When you run a command like: ```bash $ yarn global add create-react-app ``` [Yarn](https://yarnpkg.com/lang/en/) is going to install any needed dependencies including any binaries. So, where is Yarn putting these binaries? Let's ask: ```bash $ yarn global bin /Users/jbranchaud/.asdf/installs/nodejs/10.15.3/.npm/bin ``` This is where it is located on my machine. You'll want to run it on your own machine because the exact location will depend on the specifics of your installation. See [the docs](https://yarnpkg.com/lang/en/docs/cli/global/) for more details. ================================================ FILE: javascript/for-in-iterates-over-object-properties.md ================================================ # for...in Iterates Over Object Properties I don't reach for _for loops_ very often, so when I needed one recently I thought I'd check out the newer `for...in` construct. It didn't behave quite how I was expecting. I thought it would iterate over the values in the target list, instead it seemed to be iterating over the indices. The [MDN docs](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for...in) explain what is going on: > The `for...in` statement iterates over all non-Symbol, enumerable properties > of an object. An array is an object whose properties are the indices of the values stored in the array. ```javascript const fruits = ["apple", "banana", "orange"]; for (let fruit in fruits) { console.log(fruit, fruits[fruit]); } // => "0" "apple" // => "1" "banana" // => "2" "orange" ``` The iteration value wasn't what I was looking for, but in this case I can use it to access the value from the list. I'd be better off using a standard _for loop_ though. ================================================ FILE: javascript/format-a-decimal-to-a-fixed-number-of-digits.md ================================================ # Format A Decimal To A Fixed Number Of Digits The `Intl.NumberFormat` object is a reliable way to format numbers in an i18n-friendly way. It available with Node and all modern browsers. If I want to format number that I expect to contain decimals, I can do so by setting up a formatter using the `decimal` style. ```javascript const locale = "en-US"; const options = { style: "decimal", }; const formatter = new Intl.NumberFormat(locale, options); console.log(formatter.format(1234)) //=> 1,234 console.log(formatter.format(1234.5678)) //=> 1,234.568 ``` Because of my locale (`en-US`), it adds a comma to deliniate the thousandth place. By default, it formats to three decimal places excluding decimals altogether if it is a whole number. If I want to specify a fixed number of decimal places including for a whole number, I can use the `minimumFractionDigits` and `maximumFractionDigits` options. ```javascript const locale = "en-US"; const options = { style: "decimal", minimumFractionDigits: 2, maximumFractionDigits: 2, }; const formatter = new Intl.NumberFormat(locale, options); console.log(formatter.format(1234)) //=> 1,234.00 console.log(formatter.format(1234.5678)) //=> 1,234.57 ``` Here, it includes the `.00` on the whole number and it truncates the number with more than 2 decimal places rounding as necessary. See the [`Intl.NumberFormat` docs](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/NumberFormat/NumberFormat) for more details. ================================================ FILE: javascript/format-a-list-of-items-by-locale.md ================================================ # Format A List Of Items By Locale The `Intl` module includes a [`ListFormat` object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/ListFormat) which can be used to format a list of items in a consistent way across locales. I've reinvented the wheel of writing a helper function numerous times across projects for formatting a list of items that accounts for formatting based on how many items there are. This built-in function handles that with the added benefit of working across locales. Here are lists of three, two, and one items formatted in the `long` styles for US english. ```javascript > const formatter = new Intl.ListFormat('en', { style: 'long', type: 'conjunction' }); undefined > formatter.format(['Alice', 'Bob', 'Carla']) 'Alice, Bob, and Carla' > formatter.format(['Coffee', 'Tea']) 'Coffee and Tea' > formatter.format(['Taco']) 'Taco' ``` The difference between `long` and `short` style for a `conjunction` is _and_ versus _&_. In addition to the type`conjunction`, you could also use `disjunction` which will do an _or_ instead of an _and_. I'm not sure what you'd use the `unit` type for. You could use another locale, such as French, as well: ```javascript > const formatter = new Intl.ListFormat('fr', { style: 'long', type: 'conjunction' }); undefined > formatter.format(['café', 'thé']) 'café et thé' ``` ================================================ FILE: javascript/format-time-zone-identifier.md ================================================ # Format Time Zone Identifier Though there are surely libraries that can help with this task, we now have full support in the [`Intl.DateTimeFormat` API](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat/DateTimeFormat) for formatting a date's time zone identifier in various ways. To do this, we have to create a formatter specifying the locale, the `timeZone` option, and any formatting options. For the formatting, I'll try the `timeZoneName` with both `short` and `longGeneric`. Then we `formatToParts` on any date object and extract the `timeZoneName` value: ```javascript const options = { timeZone: 'America/Chicago', timeZoneName: "short" } const formatter = new Intl.DateTimeFormat("en-US", options) formatter.formatToParts(new Date()).find((part) => part.type === "timeZoneName").value //=> 'CDT' ``` Now, let's try this for `longGeneric`: ```javascript const options = { timeZone: 'America/Chicago', timeZoneName: "longGeneric" } const formatter = new Intl.DateTimeFormat("en-US", options) formatter.formatToParts(new Date()).find((part) => part.type === "timeZoneName").value //=> 'Central Time' ``` There are several more options for the `timeZoneName` as well as a bunch more you can do with the `Intl.DateTimeFormat` API. ================================================ FILE: javascript/formatting-values-with-units-for-display.md ================================================ # Formatting Values With Units For Display There are all kinds of values and units your app may need to display. The [`Intl.NumberFormat` API](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/NumberFormat) that is available in modern browsers can help with standardizing that display. How about formatting distances for display? Here is a compact formatting of miles: ```javascript > const milesFormat = Intl.NumberFormat("en-US", { style: "unit", unit: "mile" }); > milesFormat.format(1500) "1,500 mi" ``` Here is two different formats for kilometers: ```javascript > const kmFormat = Intl.NumberFormat("en-US", { style: "unit", unit: "kilometer" }); > kmFormat.format(1500) "1,500 km" > const kilometerFormat = Intl.NumberFormat("en-US", { style: "unit", unit: "kilometer", unitDisplay: "long" }) > kilometerFormat.format(1500) "1,500 kilometers" ``` Give it a try with something else like `miles-per-hour`, `liter`, or `gallon`. [source](https://twitter.com/jamesreggio/status/1196574375916400640?s=21) ================================================ FILE: javascript/freeze-an-object-sorta.md ================================================ # Freeze An Object, Sorta You can freeze a JavaScript object using `Object.freeze` which will help enforce some immutability practices. Don't be fooled though, you can still modify arrays and objects in the frozen object. Here is what the docs have to say: > The Object.freeze() method freezes an object: that is, prevents new > properties from being added to it; prevents existing properties from being > removed; and prevents existing properties, or their enumerability, > configurability, or writability, from being changed, it also prevents the > prototype from being changed. And here is `Object.freeze` in action: ```javascript > const things = {one: "two", hello: "world", cats: ["Von Neumann", "Sosa"]} undefined > Object.freeze(things) {one: "two", hello: "world", cats: Array(2)} > things.one = "three" "three" > things.dogs = [] [] > delete things.hello false > things {one: "two", hello: "world", cats: Array(2)} > things.cats.push("Sneaky") 3 > things {one: "two", hello: "world", cats: Array(3)} ``` See the [MDN Docs](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze) for more details. h/t Jake Worth ================================================ FILE: javascript/generate-a-v4-uuid-in-the-browser.md ================================================ # Generate A V4 UUID In The Browser The [Web Crypto API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Crypto_API) provides a [`randomUUID`](https://developer.mozilla.org/en-US/docs/Web/API/Crypto/randomUUID) function for generating v4 UUID values which are cryptographically random. It works like this: ```javascript > crypto.randomUUID() 'f2050c5e-af52-4ca2-b4d6-23758b3396c9' > crypto.randomUUID() '079d5186-84d4-41d6-a660-edafb6a74c86' ``` No two UUIDs will be the same. This is a great way to generate IDs that are guaranteed to be unique. Or if you need a value that is practically impossible to guess. I was surprised to see that as of writing this, this function has great browser support. Modern versions of all browsers except Internet Explorer have implemented `randomUUID`. ================================================ FILE: javascript/generate-random-integers.md ================================================ # Generate Random Integers JavaScript's `Math` module has a built-in `random` function. ```javascript > Math.random(); 0.4663311937101857 ``` It can be used to generate random floats between 0 and 1. If you want integers, though, you're going to have to build your own function. ```javascript function getRandomInt(max) { return Math.floor(Math.random() * Math.floor(max)); } > getRandomInt(10); 1 > getRandomInt(10); 7 > getRandomInt(10); 2 ``` This function allows you to randomly generate numbers between `0` (inclusive) and `max`, a number you specify (exclusive). [source](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/random) ================================================ FILE: javascript/get-the-location-and-size-of-an-element.md ================================================ # Get The Location And Size Of An Element [All modern browsers](https://caniuse.com/#feat=getboundingclientrect) ship with the [`getBoundingClientRrect()` function](https://developer.mozilla.org/en-US/docs/Web/API/Element/getBoundingClientRect). It can be invoked for any DOM element. It returns the `x` and `y` coordinates of the element within the browser viewport as well as the `height` and `width` values and the `top` and `bottom` values along the `y-axis` and `left` and `right` values along the `x-axis`. ```javascript > $0.getBoundingClientRect() { "x": 381.421875, "y": 70, "width": 1030.578125, "height": 48, "top": 70, "right": 1412, "bottom": 118, "left": 381.421875 } ``` For instance, this is the result of invoking it against a header element on the _MDN page_ linked above. ================================================ FILE: javascript/get-the-response-status-from-an-axios-error.md ================================================ # Get The Response Status From An Axios Error You can make API requests with [`axios`](https://github.com/axios/axios). If the request fails because of a 4xx or 5xx response, an error will be raised. The response to the request is stored on the error so that you can still access the details of the response in your error handling logic. Here is a snippet of code that details using async/await with `axios`. It wraps the call in a `try/catch` block. ```javascript async function getCharacter(characterId) { let response; try { response = await axios.get( `https://rickandmortyapi.com/api/character/${characterId}` ); } catch (e) { response = e.response; } console.log(response.status); // You can also access the response data // console.log(response.data); } getCharacter(2); //=> 200 getCharacter(2000); //=> 404 ``` In the case of the second call that results in a 404 response, the `catch` block is executed. This pulls the `response` off the error (`e`). Just like the standard response, the response from the error contains `status`, `data`, `headers`, etc. This also works with a promise-based control flow. [Live Example](https://codesandbox.io/s/ancient-currying-5cmgm?file=/src/index.js) ================================================ FILE: javascript/get-the-time-components-of-a-date.md ================================================ # Get The Time Components Of A Date A `Date` object in JavaScript has several functions available to it for getting at the time components of that date. ```javascript > const now = new Date() undefined > now 2023-06-14T17:44:06.425Z > now.getMinutes() 44 > now.getSeconds() 6 > now.getHours() 12 ``` For a given `Date` object, you can access the hours with [`getHours()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/getHours), the minutes with [`getMinutes()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/getMinutes), and the seconds with [`getSeconds()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/getSeconds). To round things out, there is also [`getMilliseconds()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/getMilliseconds) and [`getTimezoneOffset()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/getTimezoneOffset). ================================================ FILE: javascript/get-the-time-zone-of-the-client-computer.md ================================================ # Get The Time Zone Of The Client Computer The [`resolvedOptions`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DateTimeFormat/resolvedOptions) function on `Intl.DateTimeFormat.prototype` provides a number of pieces of information about the client computer. It includes information such as the locale and the numbering system. It also has the time zone for that machine. Try running this line of JavaScript in your own browser. ```javascript $ Intl.DateTimeFormat().resolvedOptions().timeZone ``` When I run it, I get `America/Chicago`. You can use this within your client-side code as a way of determining in which time zone your users are. ================================================ FILE: javascript/globally-install-a-package-with-yarn.md ================================================ # Globally Install A Package With Yarn I'm used to using the `-g` flag with `npm` to install packages globally. ```bash $ npm install -g create-react-app ``` But how do I achieve the same thing with [`yarn`](https://yarnpkg.com/en/)? By using the `global` command prefix, I can do a number of `yarn` tasks globally, including adding a package. ```bash $ yarn global add create-react-app ``` [source](https://yarnpkg.com/lang/en/docs/cli/global/) ================================================ FILE: javascript/globally-install-specific-version-of-pnpm.md ================================================ # Globally Install Specific Version Of PNPM There are several ways to install [`pnpm`](https://pnpm.io/). I prefer using `npm` because I already have it installed and it fits in with all the other global packages I have installed. ```bash npm install --location=global pnpm@7.13.4 ``` The `--location` flag tells `npm` to install it as a `global` package, rather than as part of my current project. The `@7.13.4` tacked onto the end of the `pnpm` package name is how I specify the version of the package I am interested in. ================================================ FILE: javascript/immutable-remove-with-the-spread-operator.md ================================================ # Immutable Remove With The Spread Operator ES6 introduces the [spread operator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_operator) which allows you to expand arrays in place for function calls, array composition, array destructuring, etc. One thing the spread operator allows you to concisely do with array composition is perform immutable operations on arrays. For instance, to remove an item from an array by index, you can throw together the following function. ```javascript const remove = (items,index) => { return [...items.slice(0,index), ...items.slice(index+1,items.length)]; }; const list = [1,2,3,4,5]; remove(list, 2); // [1,2,3,4] list // [1,2,3,4,5] ``` It only took a couple lines of code and immutability is baked in. There may be a couple edge cases that are not handled in this solution (e.g. `remove(list, -1)`), but you get the general idea. ================================================ FILE: javascript/initialize-a-new-javascript-project-with-yarn.md ================================================ # Initialize A New JavaScript Project With Yarn Yarn, like NPM, has a basic command for initializing a new JavaScript project. ``` $ yarn init ``` This will ask you a number of questions about your project like the name and what kind of license you want to use. In the end, it just creates a `package.json` file which can be used with `yarn` or `npm`. ================================================ FILE: javascript/install-the-latest-version-of-node-with-nvm.md ================================================ # Install The Latest Version Of Node With Nvm [Nvm](https://github.com/creationix/nvm), the Node Version Manager, is a great way to manage multiple versions of Node.js on a machine. Run the following command to get nvm to install the latest version of Node.js for you. ```bash $ nvm install node --reinstall-packages-from=node ``` [source](https://bytearcher.com/articles/ways-to-get-the-latest-node.js-version-on-a-mac/) ================================================ FILE: javascript/interpolate-a-string-into-a-regex.md ================================================ # Interpolate A String Into A Regex You can build up strings with concatenation or the template literal syntax. If you're incorporating variables as part of this string building, that's called interpolation. But what if you need to interpolate variables into a regular expression (regex)? A regex literal looks like this: ```javascript const myRegex = /^Some Customizable Text$/; ``` This works great for fixed strings, but won't cut it if you need to work _variable_ strings. What's needed to work with variables is the [`RegExp`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp) constructor: ```javascript const customizableString = "Some Customizable Text"; const myRegex = new RegExp(`^${customizableString}$`); ``` You can build up some string with string variables and regex syntax and `new RegExp()` will turn it into a regex. [source](https://makandracards.com/makandra/15879-javascript-how-to-generate-a-regular-expression-from-a-string) ================================================ FILE: javascript/iso-8601-formatted-dates-are-interpreted-as-utc.md ================================================ # ISO-8601 Formatted Dates Are Interpreted As UTC Using `new Date()` or `Date.parse()` with a string that represents a date is a great way to create a `Date` object for a specified date. A variety of formats are accepted by these methods. But, caution! There are subtle differences in how those dates will be interpreted. Given any old string that reasonably represents a date, the date will be interpreted using the local time zone, in my case `CST`. ```javascript > new Date('2017-12-4') Mon Dec 04 2017 00:00:00 GMT-0600 (CST) ``` However, as soon as we use an ISO-8601 compliant date format, ECMAScript 5 specifies that the date ought to be interpreted using the UTC time zone. As you can see, the results are drastic enough to affect what day it comes out to. ```javascript > new Date('2017-12-04') Sun Dec 03 2017 18:00:00 GMT-0600 (CST) ``` [Source](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/parse#ECMAScript_5_ISO-8601_format_support) ================================================ FILE: javascript/link-a-javascript-package-locally.md ================================================ # Link A JavaScript Package Locally If you are putting together a JavaScript package and you'd like to test it out locally before putting it on NPM, use `npm link`. First, from the directory of the package you are creating run: ```bash $ npm link ``` This will symlink the package to the global node modules directory. Then, from the base project directory that you want to try importing and using the package from, run: ```bash $ npm link name-of-package ``` This will create an additional symlink from the global node modules directory to the `node_modules` of this target project. You'll now have access to the project, try an `import` to get what you need and try it out. See `man npm-link` for more details. ================================================ FILE: javascript/list-top-level-npm-dependencies.md ================================================ # List Top-Level NPM Dependencies The `npm ls` command can be used to list all dependencies for a project. This will, however, produce an exhaustive list of all dependencies including dependencies of dependencies. A list this large probably isn't going to be of much use. The `--depth` flag allows you to restrict the depth of the dependency tree that is generated. ```bash $ npm ls --depth=0 ``` This will produce a list of only the top-level dependencies. See `man npm-ls` for more details. ================================================ FILE: javascript/load-and-use-env-var-in-node-script.md ================================================ # Load And Use Env Var In Node Script The node scripts defined under `scripts` in a `package.json` file can be used to do all sorts of handy development tasks for a project. There are times where those scripts would be even more useful if they had access to environment variables specified in the project's `.env` files. With the help of [`dotenv-cli`](https://github.com/venthur/dotenv-cli) and [`cross-var`](https://github.com/elijahmanor/cross-var), we can load in and reference the project's env vars. As an example, let's say our `.env` file has a `DATABASE_URL`: ``` DATABASE_URL="postgresql://postgres:postgres@localhost:5432/postgres" ``` We can create a node script in `package.json` that accesses that value like so: ```json { "scripts": { "db:url": "dotenv cross-var -- echo \"%DATABASE_URL%\"" } } ``` Running that command will echo out the value: ```bash ❯ npm run db:url > db:url > dotenv cross-var -- echo "%DATABASE_URL%" postgresql://postgres:postgres@localhost:9876/postgres ``` You could do something more useful with that value like open a `psql` connection to that local database. [source](https://www.genui.com/resources/env-variables-json) ================================================ FILE: javascript/make-the-browser-editable-with-design-mode.md ================================================ # Make The Browser Editable With Design Mode Modern browsers support a Web API called _Design Mode_. When you turn on _Design Mode_, you are able to edit the text on the web page like you would do in Word or Dreamweaver. This is a great way to experiment with copy. By default it is `"off"`, but you can turn it on from your browser's devtools console. ``` > document.designMode = "on" "on" ``` [source](https://developer.mozilla.org/en-US/docs/Web/API/Document/designMode) ================================================ FILE: javascript/make-truly-deep-clone-with-structured-clone.md ================================================ # Make Truly Deep Clone With Structured Clone There are a lot of ways to make a copy of an object. They are all hacks and they all fail in certain circumstances. Using the spread trick only gives you a shallow copy where references to nested objects and arrays can still be updated. The `JSON.stringify` trick has to make things like dates into strings, so it is lossy. There is however now a dedicated method for deep copies with broad support called [`structuredClone`](https://developer.mozilla.org/en-US/docs/Web/API/Window/structuredClone). It is available on `window`. Let's take a look at it and see how it comparse to the spread operator trick. ```javascript > // some data setup > const data = { one: 1, two: 2, rest: [3,4,5] } > const obj = { hello: 'world', taco: 'bell', data } > const shallowObj = { ...obj } > const deepObj = structuredClone(obj) > // let's modify the original `data.rest` array > data.rest.push(6) 4 > data { one: 1, two: 2, rest: [ 3, 4, 5, 6 ] } > // now let's see who was impacted by that mutation > obj { hello: 'world', taco: 'bell', data: { one: 1, two: 2, rest: [ 3, 4, 5, 6 ] } } > shallowObj { hello: 'world', taco: 'bell', data: { one: 1, two: 2, rest: [ 3, 4, 5, 6 ] } } > deepObj { hello: 'world', taco: 'bell', data: { one: 1, two: 2, rest: [ 3, 4, 5 ] } } ``` The `shallowObj` from the spread operator copy was mutated even though we didn't intend for that. The `deepObj` from `structuredClone` was a true deep copy and was unaffected. [source](https://www.builder.io/blog/structured-clone) ================================================ FILE: javascript/matching-a-computed-property-in-function-args.md ================================================ # Matching A Computed Property In Function Args The [computed property name](http://es6-features.org/#ComputedPropertyNames) feature of ES6 allows you to reference a variable in object assignments and destructurings. This syntax is flexible enough that it can be used in the arguments portion of a function declaration. In fact, it can even be matched against another argument -- allowing the creation of some handy, yet terse functions. ```javascript const get = (key, { [key]: foundValue }) => foundValue; ``` Notice that the first argument, `key`, will match against the computed property name in the second argument. The `foundValue` will correspond to whatever `key` maps to in the given object. This `get` function can then be used like so. ```javascript const stuff = { a: 1, b: 2, c: 3 }; console.log("Get a:", get("a", stuff)); // Get a: 1 console.log("Get d:", get("d", stuff)); // Get d: undefined ``` h/t [@sharifsbeat](https://twitter.com/sharifsbeat/status/1084235020183748610) ================================================ FILE: javascript/matching-multiple-values-in-a-switch-statement.md ================================================ # Matching Multiple Values In A Switch Statement Switch statements are a handy way to execute different branches of code based on a value match. This is often what is used in Redux reducers when updating the state in response to certain actions. But what if you need multiple values to result in the same branch of execution without duplicating the code? The execution of a switch statement falls through, so after one match, it will continue to try and do subsequent matches if you don't interrupt the execution with a `break` or `return`. Conveniently, this solves our problem of matching multiple values. ```javascript switch (action.type) { case "UPDATE_NAME": case "UPDATE_DURATION": let newData = anotherReducer(state.data, action); return { ...state, data: newData }; default: return state; } ``` See the [MDN docs](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/switch) for more details. ================================================ FILE: javascript/mock-a-function-with-return-values-using-jest.md ================================================ # Mock A Function With Return Values Using Jest [Jest](https://facebook.github.io/jest/) provides a collection of utilities for working with mocked functions. To create a mock function, do: ```javascript jest.fn() // assign it to a variable const fakeFunc = jest.fn(); // pass it as a prop ``` A mocked function can then be attributed with a return value. ```javascript const fakeFunc = jest.fn(); fakeFunc.mockReturnValue("hello"); fakeFunc(); // => "hello" ``` The [`mockReturnValue()`](https://facebook.github.io/jest/docs/en/mock-function-api.html#mockfnmockreturnvaluevalue) function ensures that the value is returned whenever your function is called. We can also limit the return value to occurring just once. ```javascript const fakeFunc = jest.fn(); fakeFunc.mockReturnValueOnce("hello"); fakeFunc(); // => "hello" fakeFunc(); // => null ``` [`mockReturnValueOnce()`](https://facebook.github.io/jest/docs/en/mock-function-api.html#mockfnmockreturnvalueoncevalue) ensures the value is returned once and all subsequent calls yield `null`. ================================================ FILE: javascript/new-dates-can-take-out-of-bounds-values.md ================================================ # New Dates Can Take Out Of Bounds Values You can create a new date by feeding it arguments for _year_, _month_, and _day_. ```javascript > new Date(2017, 11, 31) Sun Dec 31 2017 00:00:00 GMT-0600 (CST) ``` What happens if we push the _day_ value out of bounds? ```javascript > new Date(2017, 11, 32) Mon Jan 01 2018 00:00:00 GMT-0600 (CST) ``` It rolls over to the next month. Does the same happen when we push the _month_ value out of bounds? ```javascript > new Date(2017, 12, 31) Wed Jan 31 2018 00:00:00 GMT-0600 (CST) ``` Yep. What about negative values? ```javascript > new Date(2018, -1, 31) Sun Dec 31 2017 00:00:00 GMT-0600 (CST) ``` It rolls the month, and consequently the year, back. ================================================ FILE: javascript/numbers-are-empty.md ================================================ # Numbers Are Empty The [`lodash`](https://lodash.com/) project comes with a ton of handy JavaScript utilities including the [`_.isEmpty()`](https://lodash.com/docs#isEmpty) function. This is great for checking if Arrays, Objects, and Strings are _empty_. The following is how this function is defined in the docs: > Checks if value is an empty collection or object. A value is considered > empty if it's an arguments object, array, string, or jQuery-like > collection with a length of 0 or has no own enumerable properties. Having not examined this definition too closely and because I primarily write Rails code from day to day, I conflated `_.isEmpty()` with the `#blank?` method provided by Rails' `ActiveSupport`. This holds true for the most part, but quickly defies expectations when it comes to numbers. ```javascript > _.isEmpty(1) // true ``` ================================================ FILE: javascript/object-initialization-with-shorthand-property-names.md ================================================ # Object Initialization With Shorthand Property Names If I have some variables: ```javascript const one = 1, two = 2, three = 3; ``` and I'd like to initialize an object with them, I'll generally do something like the following: ```javascript const obj1 = { one: one, two: two, three: three }; // Object { one: 1, two: 2, three: 3 } ``` That seems pretty standard, but with ES6 comes a feature called *shorthand property names* which makes that look verbose and redundant. If you already have properly named variables, they can be used as a short hand for both the key name and variable value: ```javascript const obj2 = { one, two, three }; // Object { one: 1, two: 2, three: 3 } ``` See the [MDN Docs for Object Initializer](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Object_initializer) for more details. ================================================ FILE: javascript/obtain-undefined-value-with-the-void-operator.md ================================================ # Obtain Undefined Value With The Void Operator The [`void` operator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/void) takes any expression, evaluates it, and then results in the `undefined` value. A common use of the `void` operator is to get the primitive `undefined` value in a consistent way. ```javascript > void(0); undefined ``` This is handy for instances where you need to check if a value is `undefined`: ```javascript function doSomething({ arg }) { if (arg === void 0) { throw new Error("arg is undefined 😱"); } // ... }; ``` ================================================ FILE: javascript/open-global-npm-config-file.md ================================================ # Open Global npm Config File There are a ton of configuration settings for `npm`. I can open up the global `npmrc` file where those configs live with the following command: ```bash $ npm config edit --global ``` This will open up the file in my default editor (e.g. `vim`). There are over 100 settings that are commented out with their default values. I can override these as needed by uncommenting them and setting them to the desired value. There is some useful info in the first few lines: ``` ;;;; ; npm globalconfig file: /Users/me/.asdf/installs/nodejs/20.10.0/etc/npmrc ; this is a simple ini-formatted file ; lines that start with semi-colons are comments ; run `npm help 7 config` for documentation of the various options ``` 1. I can see that the location of the file is specific to the version of node that I current have shimmed. In this case, it's `20.10.0`. If I switch node versions, there will be a different `envrc` to configure. 2. I can get more info about these config values by running `npm help 7 config`. ================================================ FILE: javascript/parse-a-date-from-a-timestamp.md ================================================ # Parse A Date From A Timestamp If you are given a timestamp ([seconds since the Unix epoch](https://stackoverflow.com/a/20823376/535590)) and you try to parse it with [JavaScript's `new Date()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/Date), you are going to get a suprising result. ```javascript > new Date(1618499080) Mon Jan 19 1970 11:34:59 GMT-0600 (Central Standard Time) ``` 1970? I was expected something more in the current millenia. This is because JavaScript's `new Date()` expects a timestamp to be in milliseconds. Passing in a seconds representation of a timestamp, when it should be milliseconds, is going to result in a time pretty near the original Unix epoch. Instead what you need to do is multiple that _seconds_ value by `1000` to get it in terms of milliseconds. ```javascript > new Date(1618499080 * 1000) Thu Apr 15 2021 10:04:40 GMT-0500 (Central Daylight Time) ``` Also, notice that if I run [`+ new Date()`](https://stackoverflow.com/a/221297/535590) without any argument, it provides the current timestamp in milliseconds. ```javascript > + new Date() 1618499080598 ``` ================================================ FILE: javascript/pre-and-post-hooks-for-yarn-scripts.md ================================================ # Pre And Post Hooks For Yarn Scripts In [yarn](https://classic.yarnpkg.com/) v1, there is a special syntax for script names that allows you to define pre and post hooks. For instance, if you have a `build` script, you can define a `prebuild` and/or a `postbuild` script as well. Then, anytime you invoke `yarn build`, the `prebuild` script will be automatically run before `build` and the `postbuild` script be automatically run after `build`. This `pre{script}` and `post{script}` syntax works for any script. ```json { "scripts": { "predeploy": "node pre-deploy-steps" "deploy": "my-framework deploy", "postdeploy": "node post-deploy-steps" } } ``` This syntax may lead to unexpected script invocations. For instance, a `preserve` script will run ahead of a `serve` script even though those were probably intended to be unrelated scripts. This is, in part, why this syntax is no longer support in yarn v2. [source](https://classic.yarnpkg.com/lang/en/docs/cli/run/#toc-yarn-run-script) ================================================ FILE: javascript/prevent-hidden-element-from-flickering-on-load.md ================================================ # Prevent Hidden Element From Flickering On Load Here is what it might look like to use [Alpine.js](https://alpinejs.dev/) to sprinkle in some JavaScript for controlling a dropdown menu. ```html ``` Functionally that will work. You can click the button to toggle the menu open and closed. What you might notice, however, when you refresh the page is that the menu flickers open as the page first loads and then disappears. This is a quirk of the element being rendered before Alpine.js is loaded and the [`x-show`](https://alpinejs.dev/directives/show) directive has a chance to take effect. To get around this, we can _cloak_ any element with an `x-show` directive that should be hidden by default. ```html ``` This addition needs to be paired with some custom CSS to hide any _cloaked_ elements. ```css [x-cloak] { display: none !important; } ``` [source](https://alpinejs.dev/directives/cloak) ================================================ FILE: javascript/purge-null-and-undefined-values-from-object.md ================================================ # Purge Null And Undefined Values From Object The low-level utilities provided by [`lodash`](https://lodash.com/) offer a couple ways to remove all the `null` and `undefined` values from an object. First, here is an object that I want to _compact_ by removing all `nil` (`null` and `undefined`) values. ```javascript const data = { hello: 'world', list: [1,2,3], status: undefined, published_at: null, points: 0 } ``` One method of doing this is with the [`_.pickBy` function](https://lodash.com/docs/4.17.15#pickBy). ```javascript > _.pickBy(data) //=> { hello: 'world', list: [1,2,3] } ``` Because it defaults to picking _truthy_ values, the `points: 0` is also going to be stripped out. Another method which allows us to more directly target just `null` and `undefined` uses [`_.omitBy`](https://lodash.com/docs/4.17.15#omitBy) and [`_.isNil`](https://lodash.com/docs/4.17.15#isNil). ```javascript > _.omitBy(data, _.isNil) //=> { hello: 'world', list: [1,2,3], points: 0 } ``` Notice this approach only removes the `null` and `undefined` key-value pairs. The `points: 0` is left in. ================================================ FILE: javascript/random-cannot-be-seeded.md ================================================ # Random Cannot Be Seeded In JavaScript, you can use `Math.random()` to get a *sorta* random value. ```javascript > Math.random() 0.5130641541909426 ``` Most programming languages give you a method of seeding the random number generator for determinism. Unfortunately, JavaScript provides no way for choosing or resetting the seed to `Math.random()`. [source](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/random) ================================================ FILE: javascript/reach-into-an-object-for-nested-data-with-get.md ================================================ # Reach Into An Object For Nested Data With Get Among the many [lodash](https://lodash.com/) utilities is [`_.get`](https://lodash.com/docs/4.17.5#get) for getting data from nested objects. You can specify where to reach into the nested data of an object using a _path_. Consider the following awkwardly nested object: ```javascript const resp = { error: { errors: [ { message: "Something went wrong" }, ], }, }; ``` Here is how we might reach into this with vanilla JavaScript: ```javascript resp.error.errors[0].message; ``` Reaching into this for the `message` value is tricky because as soon as the `resp` object contains differently nested data, an error is likely to be thrown. We can simultaneously avoid a bunch of exception handling logic and provide a default value with the `_.get` function: ```javascript _.get(resp, 'error.errors[0].message', 'Default error message'); ``` If we decide to not include a default value, then `undefined` will be used. ================================================ FILE: javascript/render-an-array-of-elements-with-react-16.md ================================================ # Render An Array Of Elements With React 16 [React 16 was released today](https://facebook.github.io/react/blog/2017/09/26/react-v16.0.html). Among many exciting features and updates is support for rendering an array of elements. This can look as simple as this example: ```javascript return [
  • One
  • ,
  • Two
  • ,
  • Three
  • ]; ``` It really shines in the case of generating elements from an array of data. ```javascript let data = [ { value: "One", key: "1" }, { value: "Two", key: "2" }, { value: "Three", key: "3" } ]; return data.map(item => { return (
  • {item.value}
  • ); }); ``` No need to wrap the result in a `
    `! [source](https://facebook.github.io/react/docs/react-component.html#render) ================================================ FILE: javascript/resolve-and-pass-multiple-values-from-a-then.md ================================================ # Resolve And Pass Multiple Values From A Then Let's say you are chaining multiple async function calls together. ```javascript fetchTrainer(trainerName) .then(response => { const trainerData = response.body; return fetchPokemonFor({ trainerId: trainerData.id }); }) .then(response => { // I want trainerData, but it is now out of scope... }); ``` But in the last `then()` you want access to both the `trainerData` and the `pokemonData`. So, how do you pass both the `trainerData` and the resolved response of `fetchPokemonFor()` through to that last `then()`. ```javascript fetchTrainer(trainerName) .then(response => { const trainerData = response.body; return Promise.all([ trainerData, fetchPokemonFor({ trainerId: trainerData.id }) ]); }) .then(([trainerData, pokemonResponse]) => { const pokemonData = pokemonResponse.body; // do something with trainerData and pokemonData }); ``` `Promise.all` allows us to resolve and pass multiple promises. If any of the values in the array argument is not a promise, it simply passes it through. h/t Brian Dunn ================================================ FILE: javascript/run-a-bash-script-from-a-node-script.md ================================================ # Run A Bash Script From A Node Script A node script in `package.json` can reference and run any command or executable script available on our system. This is why we're able to have node scripts that do things like run a linter or start a web server. We can take this a step further by defining our own scripts to enhance our development and CI environments. For instance, let's say we have the following bash script (`check-stripe`) in our project's `scripts` directory for checking that we are connected to the right Stripe account. ```bash #!/bin/bash # Expected stripe display name EXPECTED_STRING="My Online Product" # Currently connected stripe display name COMMAND_OUTPUT=$( stripe config --list | grep '^display_name' | awk -F'=' '{print $2}' | xargs ) # Compare the expected string with the command output if [ "$EXPECTED_STRING" = "$COMMAND_OUTPUT" ]; then exit 0 else echo "Mismatched Stripe accounts, expected $EXPECTED_STRING, got $COMMAND_OUTPUT." exit 1 fi ``` This script exits with either a successful (`0`) or failed (`1`) status. We can use this in our node script to guard another command from getting run. ```json { "scripts": { "dev:stripe": "./scripts/check-stripe && stripe listen ..." } } ``` ================================================ FILE: javascript/run-multiple-node-scripts-concurrently.md ================================================ # Run Multiple Node Scripts Concurrently The [`concurrently` npm package](https://github.com/open-cli-tools/concurrently) is a CLI for concurrently running multiple commands or scripts. This is great for working in the context of a web server development where you often need several pieces of infrastructure running locally at once. Assuming you have the `concurrently` package installed and there are several dev scripts in your `package.json`, here is one way this could look: ```json { "scripts": { "dev": "concurrently \"npm run dev:next\" \"npm run dev:inngest\" \"npm run dev:mailhog\"", "dev:next": "next dev", "dev:inngest": "pnpx inngest-cli@latest dev", "dev:mailhog": "mailhog", } } ``` Running `npm run dev` would start a process that runs all three commands and combines their output. A shorthand way of writing this since these commands all have the same prefix is: ```json { "scripts": { "dev": "concurrently \"npm:dev:*\"", "dev:next": "next dev", "dev:inngest": "pnpx inngest-cli@latest dev", "dev:mailhog": "mailhog", } } ``` ================================================ FILE: javascript/running-es6-specs-with-mocha.md ================================================ # Running ES6 Specs With Mocha If your JavaScript specs contain ES6 syntax, [Mocha](https://mochajs.org/), by default, will not be able to interpret and run them. In order to run them with Mocha, you will need to tell Mocha to use something like [Babel](http://babeljs.io/) to compile them. The `--compile` flag can be used to point Mocha to the `babel-core/register` package. ``` $ mocha --compilers js:babel-core/register path/to/specs/*.spec.js ``` If you already have a test command specified in your `package.json` file, you can update it with the `--compile` portion of the above command. This all assumes you've already setup your project with Babel and Babel presets. [source](http://jamesknelson.com/testing-in-es6-with-mocha-and-babel-6/) ================================================ FILE: javascript/scoping-variables-with-a-block-statement.md ================================================ # Scoping Variables With A Block Statement A solid way to keep code readable and easy to understand is to ditch the terse one-liners in favor of breaking things up across multiple lines and using self-documenting variable names. This isn't all good though. Each new variable that is defined is a handle on some data that can be misused later in the syntax scope. JavaScript has a nice way of being clear about the scope of certain variables: ```javascript let parsedDate; { let [month, day, year] = input.split('-'); parsedDate = new Date(year, month, day); } // do something with parsedDate ``` The `month`, `day`, and `year` variables are scoped to the `{ ... }` which is a [block statement](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/block). This helps communicate and enforce that those variables are only to be used in that very specific context. Other developers and our future selves won't be able to erroneously use those variables. Of course, breaking out a function is another way of accomplishing this. Sequestering code in a different part of the file is not always the best answer though. Sometimes you want it locally fenced off. For those times, use a block statement. ================================================ FILE: javascript/short-circuit-concurrently-when-process-fails.md ================================================ # Short Circuit Concurrently When Process Fails In [Run Multiple Node Scripts Concurrently](run-multiple-node-scripts-concurrently.md), I showed how we can get all the essential processes of our web app running concurrently in development using the `concurrently` package. But what happens when there is an error and one of them exits early instead of continuing to run? Well, the rest of the processes under the `concurrently` umbrella continue to run as if nothing has happened. In fact, if there is enough output, you might even miss that that one process failed since the error gets quickly pushed off the screen. If we need every process running for our app to work, then it would be better to have the whole set of concurrent processes short circuit and exit early if one fails. We can do that by giving `concurrently` the `--kill-others-on-fail` flag. ```json { "scripts": { "dev": "concurrently --kill-others-on-fail \"npm:dev:*\"", "dev:next": "next dev", "dev:inngest": "pnpx inngest-cli@latest dev", "dev:mailhog": "mailhog", } } ``` See the [Usage section](https://github.com/open-cli-tools/concurrently?tab=readme-ov-file#usage) of the docs for more details. ================================================ FILE: javascript/show-description-of-all-npm-config-options.md ================================================ # Show Description Of All npm Config Options In [Open Global npm Config File](open-global-npm-config-file.md), I showed how you can access and modify the current npm config. The thing that stands out in that file is the sheer number of config options. What do they all mean? To get "more than you probably want to know about npm configuration", open up the npm config help with the following command: ```bash $ npm help 7 config ``` There are a ton, so browse at your own peril. You'll notice that each option lists its default value, the type of that value, and a description about usage. ================================================ FILE: javascript/sleep-for-a-bit-in-async-code.md ================================================ # Sleep For A Bit In Async Code A `sleep` utility function can be useful in a variety of situations. From testing and debugging to simulating a delay in development. Here is what that function can look like in its simplest form: ```javascript function sleep(time) { return new Promise((resolve) => { setTimeout(resolve, time) }) } ``` This function returns a promise that will resolve after the given number of milliseconds. As an example of how to use it, here is how we can simulate a delay in a fake fetch function. ```javascript async function fakeUserFetch(userId) { # add half a second of "network" latency await sleep(500) const fakeResponse = { id: userId, email: "fake-email@example.com" } return Promise.resolve(fakeResponse) } ``` [source](https://stackoverflow.com/a/951057/535590) ================================================ FILE: javascript/sorting-arrays-of-objects-with-lodash.md ================================================ # Sorting Arrays Of Objects With Lodash The [`lodash`](https://lodash.com/) library comes with a couple functions for sorting collections of objects -- [`sortBy`](https://lodash.com/docs/4.17.15#sortBy) and [`orderBy`](https://lodash.com/docs/4.17.15#orderBy). Consider the following collection of pokemon: ```javascript const pokemon = [ { name: "Pikachu", level: 12 }, { name: "Charmander", level: 12 }, { name: "Squirtle", level: 15 }, { name: "Bulbasaur", level: 11 } ]; ``` This collection can be sorted in ascending order by the value of a key in the object using `sortBy`. ```javascript import _sortBy from "lodash/sortBy"; _sortBy(pokemon, ["level"]); ``` If you want to control whether the sorting is in ascending or descending order, use `orderBy`. ```javascript import _orderBy from "lodash/orderBy"; _orderBy(pokemon, ["level"], ["desc"]); ``` You can also do sorting with primary and secondary keys by including two values in the key sort array. ```javascript import _sortBy from "lodash/sortBy"; _sortBy(pokemon, ["name", "level"]); ``` And if you want to indpendently control ascending/descending for these as well, you can. ```javascript import _orderBy from "lodash/orderBy"; _orderBy(pokemon, ["level", "name"], ["desc", "asc"]); ``` Check out the [live example](https://codesandbox.io/s/jolly-ardinghelli-cem7t) to see it in action. ================================================ FILE: javascript/splat-arguments-to-a-function.md ================================================ # Splat Arguments To A Function Often times you have a function that takes a certain set of arguments. Like the following `adder` function: ```javascript var adder = function(a,b,c) { return a + b + c; }; ``` But you are left trying to pass in arguments as an array (e.g. `[1,2,3]`). You want to be able to *splat* the array of arguments so that it matches the function declaration. This can be done by using `apply`. ```javascript > adder.apply(undefined, [1,2,3]) 6 ``` ================================================ FILE: javascript/spread-merging-objects-includes-nil-values.md ================================================ # Spread Merging Objects Includes Nil Values A handy way to merge two objects together with ES6 JavaScript syntax is to use the [spread operator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax). ```javascript const combinedObj = { ...obj1, ...obj2 }; ``` All the key-value pairs from each object are combined into a new object. For any overlapping keys, the last occurrence's value will take precedence. That bit about precedence is true for _nil_ values – `null` and `undefined` – as well. ```javascript const obj1 = { taco: 'bell', hello: 'world', list: [1,2,3] } const obj2 = { burrito: 'house', hello: null, list: undefined } const combinedObj = { ...obj1, ...obj2 } //=> { taco: 'bell', hello: null, list: undefined, burrito: 'house' } ``` Notice that even though there are "actual" values for the `hello` and `list` keys in `obj1`, they are overridden by the `null` and `undefined` values in `obj2`. ================================================ FILE: javascript/spread-the-rest-with-es6.md ================================================ # Spread The Rest With ES6 The spread operator provided by ES6 is a powerful syntactic feature. One way it can be used is to capture the _rest_ of an object or array in a variable. ```javascript const pokemon = ["Charmander", "Squirtle", "Bulbasaur"]; const [first, ...rest] = pokemon; console.log("Remaining: ", rest); // Remaining: ["Squirtle", "Bulbasaur"] const gymLeaders = { brock: "rock", misty: "water", surge: "electric", erika: "grass" }; let { brock, erika, ...otherLeaders } = gymLeaders; console.log(otherLeaders); // Object {misty: "water", surge: "electric"} ``` Using this spread destructuring we can capture the remaining parts of an array or object in a variable. We can also use this syntax in a function signature to grab specific items from an incoming object argument without losing track of the rest -- this is especially useful in React.js development when dealing with incoming props. This is a [stage 4 feature](https://github.com/tc39/proposal-object-rest-spread) and may not be available in your particular environment. See a [live example here](https://codesandbox.io/s/ov2xr1o12y). ================================================ FILE: javascript/start-node-process-in-specific-timezone.md ================================================ # Start Node Process In Specific Timezone When running a node process on your machine locally, it will adopt your machine's local timezone. I can observe this by starting a `node` process and outputting a date with `toLocaleString()`. ```javascript > new Date().toLocaleString() '11/30/2020, 8:48:17 PM' ``` This is the time that I'm writing this post, in Chicago (CST). I can then start the process in another timezone, such as UTC. ```bash $ TZ=utc node ``` With that `node` process, I can now do the same experiment. ```javascript > new Date().toLocaleString() '12/1/2020, 2:52:40 AM' ``` The time jumps ahead about 6 hours because it is going from CST (UTC-6) to UTC. Similarly, I could start the Node process for the west coast like so, ```bash $ TZ='America/Los_Angeles' node ``` ================================================ FILE: javascript/string-interpolation-with-template-literals.md ================================================ # String Interpolation With Template Literals ES6 adds support for [template literals](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals). Template literals make it much easier to compose strings of content -- string interpolation. They allow for single and double quotes without having to fuss with escaping. Embedded expressions are also supported which means you can avoid awkward-to-type string concatenation with the `+` operator. Here is an example: ```javascript > let movie = 'it' undefined > `What's up, I just saw "${movie.toUpperCase()}".` "What's up, I just saw "IT"." ``` ================================================ FILE: javascript/support-nested-matching-in-custom-jest-matchers.md ================================================ # Support Nested Matching In Custom Jest Matchers A [custom Jest matcher](define-a-custom-jest-matcher) can use standard JavaScript operations to evaluate if the given value(s) should pass or not. ```javascript expect.extend({ toContainValue(receivedArray, containedValue) { const pass = receivedArray.some(value => value === containedValue); // return formatted pass/not-pass objects with messages return { ... } } }); ``` This approach alone doesn't support the power of Jest's nested matchers. Consider trying to use this like so: ```javascript expect(['a', 2, true]).toContainValue(expect.any(Number)); ``` This would fail, even though there is a value in there that matches `any(Number)`. Jest ships with some [Jasmine](https://jasmine.github.io/) utilities that you can use, just as Jest does internally, to perform nested matching: ```javascript const { equals } = require("expect/build/jasmineUtils"); expect.extend({ toContainValue(receivedArray, containedValue) { const pass = receivedArray.some(value => equals(value, containedValue)); // return formatted pass/not-pass objects with messages return { ... } } }); ``` That `equals` utility knows how to compare raw values like integers, booleans, and even whole objects against nested `expect` matchers. [source](https://github.com/facebook/jest/issues/8295#issuecomment-482545274) ================================================ FILE: javascript/tell-jest-to-focus-on-running-only-one-test.md ================================================ # Tell Jest To Focus On Running Only One Test Test output can be noisy. Sometimes one test is inadvertently dependent on another. These are some of the reasons you may want to tell [Jest](https://jestjs.io/) to focus in and only run one particular `test` block. You can do this by calling [`test.only()`](https://jestjs.io/docs/setup-teardown#general-advice) instead of `test()`. Find the test block you are interested in focusing on and update it to look like this: ```javascript // tests above ... test.only('ensure the function returns the value', () => { // ... // test implementation // ... }) // tests below ... ``` With that 5-character addition (`.only`) we instruct Jest to run that one test while skipping all others. This is particularly useful when you are doing some `console.log` debugging of a test. You can eliminate any confusion about which test is logging out by only running one test. [source](https://stackoverflow.com/a/42828586/535590) ================================================ FILE: javascript/tell-node-to-treat-js-files-as-esm.md ================================================ # Tell Node To Treat JS Files As ESM By default, Node will treat all `.js` files as CommonJS. That means you'll need to use the CommonJS export and require syntax to share code between files. If you'd instead like to opt-in to ESM (ECMAScript Modules), you'll need to update your `package.json`. Add the following line: ```json "type": "module", ``` This will tell node that instead of CJS, it should treat all `.js` files as ESM. This means that ESM-based `export` and `import` syntax will work. This also means you don't need to be defining your files with the `.mjs` extension. For more extensive reading on this, see: - [ECMAScript Modules in Node.js](https://www.typescriptlang.org/docs/handbook/esm-node.html) - [Getting Started with (and Surviving) Node.js ESM](https://formidable.com/blog/2021/node-esm-and-exports/) - [`.mts` is a cool file extension (TypeScript ES modules)](https://mtsknn.fi/blog/mts-file-extension/) ================================================ FILE: javascript/tell-prettier-to-not-format-a-statement.md ================================================ # Tell Prettier To Not Format A Statement [Prettier](https://prettier.io/) is a boon to productivity because individuals and teams don't have to make any decisions about the fine details of how their code is formatted. Generally, let `prettier` do its thing. There are some situations where you want to preserve your own formatting, especially if it improves readability. Here is some `prettier` formatted code: ```javascript const relativeCoords = { A: [xPos - 1, yPos - 1], B: [xPos, yPos - 1], C: [xPos + 1, yPos - 1], D: [xPos - 1, yPos], E: [xPos + 1, yPos], F: [xPos - 1, yPos + 1], G: [xPos, yPos + 1], H: [xPos + 1, yPos + 1], }; ``` Originally, I included some whitespace to keep things visually aligned. If I include a `prettier-ignore` comment, the statement immediately following it will not be touched by prettier. ```javascript // prettier-ignore const relativeCoords = { A: [xPos - 1, yPos - 1], B: [xPos , yPos - 1], C: [xPos + 1, yPos - 1], D: [xPos - 1, yPos ], E: [xPos + 1, yPos ], F: [xPos - 1, yPos + 1], G: [xPos , yPos + 1], H: [xPos + 1, yPos + 1], }; ``` [source](https://prettier.io/docs/en/ignore.html#javascript) ================================================ FILE: javascript/test-coverage-stats-with-jest.md ================================================ # Test Coverage Stats With Jest [Jest](https://facebook.github.io/jest/) is a delightful tool for JavaScript testing from Facebook. As your project evolves and you add tests (or perhaps choose to not add tests) you may wonder what kind of test coverage you have. What is the overall coverage? Which files are well covered and which are seriously lacking? Use the `--coverage` flag to get this information. ```bash $ jest --coverage ``` After running all of your tests a table will be output to the terminal with a list of files and their respective coverage percentages in terms of statement, branch, function, and line coverage. ``` ---------------------------|----------|----------|----------|----------|----------------| File | % Stmts | % Branch | % Funcs | % Lines |Uncovered Lines | ---------------------------|----------|----------|----------|----------|----------------| ``` This can help guide you to what parts of your app may need more testing. ================================================ FILE: javascript/test-timing-based-code-with-jest-fake-timers.md ================================================ # Test Timing-Based Code With Jest Fake Timers In real-world code we use timeouts to do things like debouncing and throttling of functions. This is really hard to test efficently and accurately with basic test runner tooling. Jest, however, offers some [Timer Mock tooling](https://jestjs.io/docs/en/timer-mocks) that removes most of the complexity of getting this right. Here is a method to test: ```javascript const doSomethingAfter200ms = doSomething => { setTimeout(() => { doSomething(); }, 200); }; ``` A test that shows this to work would have to observe `doSomething` getting called after 200ms. The following test won't work because the expectation is evaluated before the timeout function is triggered. ```javascript describe("doSomethingAfter200ms", () => { test("does something after 200ms (fail)", () => { const doSomething = jest.fn(); doSomethingAfter200ms(doSomething); expect(doSomething).toHaveBeenCalled(); }); }); ``` By activating `jest.useFakeTimers()`, we can simulate the passing of 200ms and then check that our mocked function was called. ```javascript jest.useFakeTimers(); describe("doSomethingAfter200ms", () => { test("does something after 200ms (pass)", () => { const doSomething = jest.fn(); doSomethingAfter200ms(doSomething); jest.advanceTimersByTime(201); expect(doSomething).toHaveBeenCalled(); }); }); ``` Jest's function mocks and timer mocks make this possible. ================================================ FILE: javascript/the-comma-operator.md ================================================ # The Comma Operator I was surprised to see the result of this line of code: ```javascript const what = (1,2,3,4); console.log(what); //=> 4 ``` I asked around on [twitter](https://twitter.com/jbrancha/status/1084936559323951105) and learned that the syntax construct at play here is the [comma operator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Comma_Operator). > The comma operator evaluates each of its operands (from left to right) and > returns the value of the last operand. And that is why `what` gets bound to `4`. ================================================ FILE: javascript/throttling-a-function-call.md ================================================ # Throttling A Function Call Imagine you have a JavaScript function that makes a request to your server. Perhaps it is sending user input from a `textarea` to be processed by the server. You may want to wrap this function in a keyboard event listener so that you are sure to react immediately to any user input. However, as the user starts typing away into this text area you may find that way to many requests are being fired off to the server. The request needs to be *throttled*. You can roll your own approach to sufficiently intermittent server calls. Though, it turns out that [underscore.js](http://underscorejs.org/) comes with two functions out of the box for this kind of behavior. - [`throttle`](http://underscorejs.org/#throttle) will give you a function that wraps your function in a way that essentially rate-limits it to being called at most once every `N` milliseconds. - [`debounce`](http://underscorejs.org/#debounce), on the other hand, will give you a function that only calls your function once `N` milliseconds has passed since it was last called. These are two subtly different approaches to making sure a function gets called, just not too often. h/t [Jake Worth](https://twitter.com/jwworth) ================================================ FILE: javascript/timing-processes.md ================================================ # Timing Processes If you want to time a process, you can use the `console.time()` and `console.timeEnd()` utilities specified by the `console` Web API. Invoking `console.time()` with a label starts a named timer. You can then run the process you want to time. Then invoke `console.timeEnd()` with the same label to terminate the timer and see how long the process took. ```javascript console.time('sorting'); [11,10,9,8,7,6,5,4,3,2,1].sort(); console.timeEnd('sorting'); > sorting: 0.278ms console.time('console logging'); console.log('logging to the console'); console.timeEnd('console logging'); > logging to the console > console logging: 0.311ms console.time('adding'); 1 + 1; console.timeEnd('adding'); > adding: 0.006ms ``` These functions are implemented in most modern browsers. See [the docs](https://developer.mozilla.org/en-US/docs/Web/API/Console/time) for more details. ================================================ FILE: javascript/transforming-es6-and-jsx-with-babel-6.md ================================================ # Transforming ES6 and JSX With Babel 6 With Babel 5, transforming ES6 and JSX into ES5 code was accomplished by including the `babel-loader`. This would be configured in `webpack.config.js` with something like the following: ```javascript module: { loaders: [ { test: /\.jsx?$/, exclude: /node_modules/, loader: 'babel-loader', } ], }, ``` Now, with Babel 6, the different parts of the loader have been broken out into separate plugins. These plugins need to be installed ``` $ npm install babel-preset-es2015 babel-preset-react --save-dev ``` and then included as presets ```javascript module: { loaders: [ { test: /\.jsx?$/, exclude: /node_modules/, loader: 'babel-loader', query: { presets: ['es2015', 'react'] }, } ], }, ``` Alternatively, the presets can be specified in the project's `.babelrc` file. [Source](http://jamesknelson.com/the-six-things-you-need-to-know-about-babel-6/) ================================================ FILE: javascript/truthiness-of-integer-arrays.md ================================================ # Truthiness of Integer Arrays We can consider the truthiness of `[1]` as follows: ```javascript > [1] == true => true > Boolean(true) => true > Boolean([1]) => true ``` We can consider the truthiness of `[0]` as follows: ```javascript > [0] == false => true > Boolean(false) => false > Boolean([0]) => true ``` The truthiness of `[0]` does not seem to be consistent. See this [JavaScript Equality Table](https://dorey.github.io/JavaScript-Equality-Table/) for more details. ================================================ FILE: javascript/turn-an-html-collection-into-an-array.md ================================================ # Turn An HTMLCollection Into An Array If you are using any of the built-in document query utilities, such as [`getElementsByClassName()`](https://developer.mozilla.org/en-US/docs/Web/API/Document/getElementsByClassName): ```javascript > document.getElementsByClassName("some-class") HTMLCollection(5) [ ... ] ``` Then you are going to get an [`HTMLCollection`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLCollection) in response. An instance of this cannot be iterated over, so you cannot call things like `map` and `filter` against it. To use Array collection methods, you are first going to need to turn the `HTMLCollection` into an array. You can do this with [`Array.from`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/from): ```javascript > Array.from(document.getElementsByClassName("some-class")) [ ... ] ``` [source](https://medium.com/@chuckdries/traversing-the-dom-with-filter-map-and-arrow-functions-1417d326d2bc) ================================================ FILE: javascript/turn-off-console-error-messages-in-a-test.md ================================================ # Turn Off Console Error Messages In A Test I'm using [Jest](https://facebook.github.io/jest/) to test a React component that requires a prop via [PropTypes](https://reactjs.org/docs/typechecking-with-proptypes.html). In one of my tests, I want to test that component when the required prop is excluded. The side effect of doing this is that my test output gets cluttered with the PropType warning. The thing to do is silence the error message during that test. ```javascript it('renders a component without a required prop', () => { const originalError = console.error; console.error = jest.fn(); // test code here expect(shallow()).toDoSomething; console.error = originalError; }); ``` We can silence `console.error` by temporarily replacing it with a Jest-mocked function and then putting it back at the end of the test. ================================================ FILE: javascript/turn-off-npm-funding-message.md ================================================ # Turn Off npm Funding Message When you run `npm install`, you'll see a few different details output to your terminal. One you are probably used to seeing is the one about packages looking for funding. ```bash ❯ npm install up to date, audited 253 packages in 2s 88 packages are looking for funding run `npm fund` for details 6 vulnerabilities (1 low, 4 moderate, 1 high) ``` If for whatever reason you need to turn off this (e.g. for scripting and CI purposes), you can globally configure it with the `npm config` command. ```bash ❯ npm config set fund false --location=global ``` Run an `npm install` again and the funding message will no longer show. This edits the `npmrc` located in the `/etc` directory of wherever your current version of Node is installed. Running `npm config edit --global` will open you up to this file as well. h/t Dillon Hafer ================================================ FILE: javascript/waiting-on-multiple-promises.md ================================================ # Waiting On Multiple Promises You may find yourself in a situation where you have to request multiple resources from multiple API endpoints and then combine that data in some way. One way to achieve this would be with nested promises. ```javascript fetch('/blogs').then((response) => { let blogs = response.body; fetch('/tags').then((response) => { let tags = response.body; // combine blogs and tags ... }) }) ``` This nesting isn't ideal and it can get hard to read as the full implementation is put into place. The `Promise` API provides an alternative. ```javascript let blogsPromise = fetch('/blogs') let tagsPromise = fetch('/tags') Promise.all([blogsPromise, tagsPromise]).then(([blogsResp, tagsResp]) => { // combine blogs and tags ... }) ``` With [`Promise.all()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all) we are able to wrap any number of promises and wait for all of them to resolve until we do something with the results. This allows us to create a context in which we have all the data we need without a bunch of nesting. ================================================ FILE: javascript/who-am-i-npm-edition.md ================================================ # Who Am I: NPM Edition Want to know who has been configured as the current user via the NPM CLI? ```bash $ npm whoami ``` See `man npm-whoami` for more details. ================================================ FILE: javascript/write-a-javascript-object-to-a-json-file.md ================================================ # Write A JavaScript Object To A JSON File To write a JavaScript object to a file as JSON, I need to use two concepts. First, I need to use [`JSON.stringify`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify) to conver the object to a string of valid JSON. Plus, by specifying the third argument as `2`, I get an indentation of two spaces as it formats the JSON. ```javascript JSON.stringify(data, null, 2) ``` Second, I need to import the `fs` (filesystem) package so that I can write the JSON string to a file. In this example, I've chosen to use the synchronous version ([`writeFileSync`](https://nodejs.org/api/fs.html#fswritefilesyncfile-data-options)) to keep my function simple. There is also an async version. ```javascript import fs from 'fs' fs.writeFileSync('my-data.json', json_string, 'utf8') ``` Here is a full example of what this could look like: ```javascript import fs from 'fs' const writeJsonToFile = (path, data) => { try { fs.writeFileSync(path, JSON.stringify(data, null, 2), 'utf8') console.log('Data successfully saved to disk') } catch (error) { console.log('An error has occurred ', error) } } const data = { name: 'Super Software LLC', orgIds: [1,3,7,11] } writeJsonToFile('my-data.json', data) ``` ================================================ FILE: javascript/yarn-commands-without-the-emojis.md ================================================ # Yarn Commands Without The Emojis If you are a hater and you'd like to run [`yarn`](https://yarnpkg.com/en/) commands without emojis being playfully included in the output, just include the `--no-emoji` flag. The output of a command like `add` will look like this: ```bash $ yarn add chalk --no-emoji yarn add v0.17.10 [1/4] Resolving packages... [2/4] Fetching packages... [3/4] Linking dependencies... [4/4] Building fresh packages... success Saved lockfile. success Saved 7 new dependencies. ├─ ansi-styles@3.1.0 ├─ chalk@2.0.1 ├─ color-convert@1.9.0 ├─ color-name@1.1.3 ├─ escape-string-regexp@1.0.5 ├─ has-flag@2.0.0 └─ supports-color@4.2.0 Done in 0.54s. ``` See `yarn help` for details. ================================================ FILE: javascript/yup-schemas-are-validated-asynchronously.md ================================================ # Yup Schemas Are Validated Asynchronously [Yup](https://github.com/jquense/yup) provides a flexible object schema validation DSL. For instance, if you want to enforce that a certain value is a number, you can define something like this: ```javascript const numSchema = yup.number(); ``` You can then validate anything against that schema. ```javascript const validator = (val) => { numSchema.validate(val) .then(result => { console.log(result); // it is the value of `val` return true; }) .catch(error => { console.log(error.errors); // array of validation error messages return false; }); }; ``` The validation is async, so if it succeeds the `then` block is hit. If the validation fails, it will fall through to the `catch`. ```javascript validator(5) // => true validator('what') // => false ``` ================================================ FILE: jj/colocate-jj-and-git-directories-for-project.md ================================================ # Colocate jj And git Directories For Project When doing a standard clone of a git repository with `jj`, you'll get a copy of the project with a `.jj` directory containing the version control information. ```bash $ jj git clone git@github.com:jbranchaud/my-repo Fetching into new repo in "/path/of/local/repo" ... $ exa --tree --all -L 1 . ├── .gitignore ├── .jj ├── Cargo.lock ├── Cargo.toml └── src ``` This is fine if I'm completely familiar with using [jujutsu](https://martinvonz.github.io/jj/latest/). However, if I'm coming from `git` and still learning, then it would be nice to be able to fallback to familiar `git` commands when needed. But without a `.git` directory, I get this: ```bash $ git log fatal: not a git repository (or any of the parent directories): .git ``` When cloning a git repo with `jj`, I can instruct it to _colocate_ which means that it will create both the `.jj` and the `.git` data directories in the project. ```bash $ jj git clone --colocate git@github.com:jbranchaud/my-repo Fetching into new repo in "/path/of/local/repo" ... $ exa --tree --all -L 1 . ├── .git ├── .gitignore ├── .jj ├── Cargo.lock ├── Cargo.toml └── src ``` Now I can run `jj` commands or `git` commands: ```bash $ git log commit 0c72abbb83657096677f9a3d5ddc7bce20839165 (HEAD, origin/trunk, trunk) ... ``` [source](https://martinvonz.github.io/jj/latest/git-compatibility/#co-located-jujutsugit-repos) ================================================ FILE: jj/describe-current-changes-and-create-new-change.md ================================================ # Describe Current Changes And Create New Change One of the first patterns I learned with `jj` was a pair of commands to essentially "commit" the working copy and start a fresh, new change. So if I am done making some changes, I can add a description to the `(no description)` working copy and then start a new working copy _change_. ```bash $ jj describe -m "Add status subcommand to show current status" $ jj new ``` I learned from [Steve](https://steveklabnik.com/) in the [jj discord](https://discord.gg/dkmfj3aGQN) that a shorthand for this pattern is to use the `jj commit` command directly. > When called without path arguments or `--interactive`, `jj commit` is > equivalent to `jj describe` followed by `jj new`. That means, instead of the above pair of commands, I could have done: ```bash $ jj commit -m "Add status subcommand to show current status" ``` That would have had the same result in my case. However, notice the caveats mentioned in the quote above and check out `man jj-commit` for more details on that. ================================================ FILE: jj/find-system-wide-config-file-for-user.md ================================================ # Find System-wide Config File For User The `jj` CLI can be configured in a couple different places. When I recently ran a `jj config` command, I was curious where specifically it was getting set. Those changes didn't appear in the repo's config (`./.jj/repo/config.toml`). That makes sense since it would only apply to that repo. So, where is the system-wide config file? The following commond shows where on your machine it is located. ```bash $ jj config path --user /Users/jbranchaud/Library/Application Support/jj/config.toml ``` Now, the next time I set a config like this: ```bash $ jj config set --user ui.paginate never ``` or want to check what other config options are set to, I can visit that path and take a look. [source](https://github.com/martinvonz/jj/blob/main/docs/config.md) ================================================ FILE: jj/squash-changes-into-parent-commit-interactively.md ================================================ # Squash Changes Into Parent Commit Interactively While I have some changes in progress as part of the working copy, I can squash them into the previous / parent commit with the `jj squash` command. Running that command as is will apply all the working copy changes to the parent leaving the current revision empty. I can also interactively squash those changes similar in spirit to how I might use `git add --patch` to stage and then amend specific changes into the previous commit with `git`. This can be done with [`jj`](https://github.com/jj-vcs/jj) using `squash` with the `-i` flag. ```bash jj squash -i # or --interactive ``` This will open up a TUI where I can click around or use keys. Each file in the source revision (in my case, the working copy) will be listed. I can move the cursor between them hitting _space_ to toggle them in or out of the squash selection. I can also hit `f` over a given file to toggle _folding_. When folding is on, a diff of the file will be disclosed with checkboxes for toggling individual hunks and lines. Once I'm satisfied with my interactive selection, I can hit `c` to confirm and only the selected files and changes will be squashed into the parent. See `man jj-squash` for more details. [source](https://steveklabnik.github.io/jujutsu-tutorial/real-world-workflows/the-squash-workflow.html) ================================================ FILE: jq/combine-an-array-of-objects-into-a-single-object.md ================================================ # Combine An Array Of Objects Into A Single Object If you've spent any amount of time pulling data out of a JSON file with `jq`, you may have run into a result set that looks a little too spacious. It's this array of single key-value pair objects. ```bash $ jq '.items | map({(.slug)}: .amount})' my-data.json [ { "key-1": 123 }, { "key-2": 345 }, { "key-3": 456 }, ... ] ``` When what you really wanted was a single object full of those unique key-value pairs. That query has you 90% of the way there. The trick is to pipe that array through the [`add` function](https://jqlang.github.io/jq/manual/#add) which will combine each of those individual objects into a single object. ```bash $ jq '.items | map({(.slug)}: .amount}) | add' my-data.json { "key-1": 123, "key-2": 345, "key-3": 456, ... } ``` ================================================ FILE: jq/count-each-collection-in-a-json-object.md ================================================ # Count Each Collection In A JSON Object Let's say your JSON file is an object that represents several different collections (arrays) of data. ```json { "users": [ ... ], "orders": [ ... ], "carts": [ ... ] } ``` We can get a nice summary of the counts of those collections using [`jq`](https://stedolan.github.io/jq/). We can do that with the [`with_entries` function](https://stedolan.github.io/jq/manual/#to_entries,from_entries,with_entries). We preserve the key (name) of each collection and then process each list with the `length` function. ```bash jq '. | with_entries({ "key": .key, "value": (.value | length)})' data.json { "users": 1234, "orders": 5432, "carts": 89 } ``` The `with_entries` function essentially maps over each key-value pair processing it with the given expression. It will then convert that `{"key": some_key, "value": 123}` mapping back into a key-value pair that gets combined with all the others. [source](https://til.simonwillison.net/jq/flatten-nested-json-objects-jq) ================================================ FILE: jq/count-the-number-of-things-in-a-json-file.md ================================================ # Count The Number Of Things In A JSON File JQ is a great tool for finding out the number of things in a JSON file. If the top-level contents of the JSON is a list, then you can pipe it directly to the [`length` function](https://stedolan.github.io/jq/manual/#length). ```bash // [1, 2, {"three": 4}] $ jq '. | length' data.json 3 ``` It works the same for counting the number of entries (key-value pairs) in a top-level JSON object. ```bash // { "hello": "world", "list": [1,2,3] } $ jq '. | length' data.json 2 ``` If you are trying to get the count of a nested value, navigate to it and then pipe that to `length`. ```bash // { "hello": "world", "list": [1,2,3] } $ jq '.list | length' data.json 3 ``` You can even count each value in a JSON object by transforming it into an array of the values with `[]`. ```bash // { "hello": "world", "list": [1,2,3] } $ jq '.[] | length' data.json 5 3 ``` Notice, the length of `"world"` is `5` characters and the length of `[1,2,3]` is `3` elements. ================================================ FILE: jq/extract-a-list-of-values.md ================================================ # Extract A List Of Values [`jq`](https://stedolan.github.io/jq/) can be used to extract a list of values from an array in a JSON file. Consider the following JSON: ```json { "items": [ { "id": 1, "name": "Liz Lemon" }, { "id": 2, "name": "Pete Hornberger" }, { "id": 3, "name": "Tracy Jordan" }, { "id": 4, "name": "Jenna Maroney" } ] } ``` First, you need to specify the selector path to the list. One it's own that will grab the list. ```bash $ jq '.items' data.json [ { "id": 1, "name": "Liz Lemon" }, ... ] ``` Then you need to use brackets to interact with the list. `.items[0]` and `.items[1]` would grab the first and second item in the list, respectively. Leaving the brackets empty (`.items[]`) tells `jq` that you want to interact with each item in the list. Then tack on a selector for what you want out of each item. ```bash $ jq '.items[].name' data.json "Liz Lemon" "Pete Hornberger" "Tracy Jordan" "Jenna Maroney" ``` This grabs the name from each of the objects within the _items_ array. The results are printed to stdout. ================================================ FILE: jq/filter-out-results-based-on-list-of-values.md ================================================ # Filter Out Results Based On List Of Values Let's say we have an array of objects in a JSON file. We want to extract some data about each of those objects, but first we want to filter out some of the objects that we don't need. This will be based on a list of IDs. The JSON might look something like this: ```json [ {'id': '123', ...}, {'id': '456', ...}, {'id': '789', ...}, {'id': '963', ...}, ... ] ``` With the [`select`](https://jqlang.github.io/jq/manual/#select) function, we can filter the array down to those objects whose IDs are [`not`](https://jqlang.github.io/jq/manual/#and-or-not) [`inside`](https://jqlang.github.io/jq/manual/#inside) the list of IDs to exclude. ``` jq '.[] | select([.id] | inside["456", "963"] | not)' data.json ``` Inside that `select`, we grab the `id` as a single value array, check if that value is inside our _exclude_ array, and then invert that result. If there is a match, that object will be filtered out. We can then chain additional filtering and extraction on to the end of the query to produce the result we want. ================================================ FILE: jq/find-all-objects-in-an-array-where-key-is-set.md ================================================ # Find All Objects In An Array Where Key Is Set Let's say we have a large array of objects. And the data in those objects, while generally having the same shape, does not always have certain values set. For instance, here is some data where the `token` is sometimes set, sometimes `null`, and sometimes missing altogether. ```json [ { "id": 1, "token": "abc" }, { "id": 3, "token": null }, { "id": 5 }, ... ] ``` We can find out how many objects in this collection have the `token` key set to an actual value with [a `jq` query](https://stedolan.github.io/jq/manual) like the following. ```bash jq '. | map(select(has("token") and .token != null)) | length' data.json ``` This maps over each object selecting those where it has `token` and `token` is not `null`. We can instead produce the inverse count—those objects where `token` is not set to a value—with the `not` operator. ```bash jq '. | map(select(has("token") and .token != null | not)) | length' data.json ``` If you want to inspect the array of objects that either of these queries filters down to, you can drop the `| length` part. ```bash jq '. | map(select(has("token") and .token != null))' data.json ``` Here is [a live example](https://jqterm.com/?query=.%20%7C%20map%28select%28has%28%22token%22%29%20and%20.token%20!%3D%20null%20%7C%20not%29%29). ================================================ FILE: jq/find-all-objects-with-a-matching-key-value-pair.md ================================================ # Find All Objects With A Matching Key Value Pair Let's say I have a JSON file representing a bunch of people's reading lists. That means it is an array of objects where each object is a person's reading profile and contains a _list_ of books. Some of those books have a _status_ of `reading` meaning the person is currently reading that book. How can we find all books that are currently being read? ``` jq '. | map( { name: .username, in_progress_books: (.books | map(select(.status == "reading"))) } )' ``` ``` [ { name: 'bobr', in_progress_books: [...] }, { name: 'sallyf', in_progress_books: [...] }, ... ] ``` That will show us for each reader what books they are currently reading. Alternatively, we could roll that all up into a single list of books. ``` jq '. | map(.books | map(select(.status == "reading"))) | flatten' ``` ``` [ { title: 'Moby Dick', status: 'reading', ... } { title: 'The Great Gatsby', status: 'reading', ... } ... ] ``` [source](https://stackoverflow.com/a/18608100/535590) ================================================ FILE: jq/get-a-slice-of-the-ends-of-an-array.md ================================================ # Get A Slice Of The Ends Of An Array [`jq`](https://jqlang.github.io/jq/) has an array slice syntax that allows us to grab a subset of values from an array. The general form of this syntax is `[n:m]` where `n` is the index of the start of our slice (inclusive) and `m` is the index of the end of our slice (non-inclusive). If we omit the `n`, it defaults to `0`, or the start of the array. Similarly, if we omit the `m`, it defaults to the end of the list. Knowing that, we can grab the first few elements of the list like so: ```bash ❯ echo '["a","b","c","d","e","f","g"]' | jq '.[:3]' [ "a", "b", "c" ] ``` We can also use a negative index value to count back from the end of the array. This allows us to grab a slice from some point relative to the end of the list. Instead of having to compute it based on knowing the length of the array. Knowing that, we can grab the last few elements of the list like so: ```bash ❯ echo '["a","b","c","d","e","f","g"]' | jq '.[-3:]' [ "e", "f", "g" ] ``` [source](https://jqlang.github.io/jq/manual/#array-string-slice) ================================================ FILE: jq/get-the-first-item-for-every-top-level-key.md ================================================ # Get The First Item For Every Top-Level Key Let's say we have a JSON object where each key in that object is tied to a large array of values. To get a better idea of what the data looks like, we want to trim down the JSON a bit. Essentially, we want to tie each key to the first value in each of their respective arrays. We can do this by reducing the object into a new object where each top-level key is tied to the first item in its array. ```bash $ jq '. as $object | reduce keys[] as $k ({}; .[$k] = $object[$k][0])' data.json ``` This uses variables (`$object` and `$k`) and the [`reduce` function](https://stedolan.github.io/jq/manual/#Reduce) to iterate over the incoming JSON object and produce a new object with the trimmed down data. JSON like this: ```json { "key1": ["a", "b", "c"], "key2": [1, 2, 3], "key3": ["x", "y", "z"] } ``` will be turned into this: ```json { "key1": "a", "key2": 1, "key3": "x" } ``` ================================================ FILE: jq/get-the-last-item-from-an-array.md ================================================ # Get The Last Item From An Array There are two ways to get the last item from an array using [`jq`](https://jqlang.github.io/jq/). The one that is perhaps a bit more intuitive is to pipe the array to the [`last`](https://jqlang.github.io/jq/manual/#first-last-nth-2) function. ```bash $ echo '[1,2,3]' | jq '. | last' 3 ``` Another approach is to use an [array index expression](https://jqlang.github.io/jq/manual/#array-index) to positionally grab the last element of the array. As is the case with some languages and libraries, `-1` positionally refers to the last item in the array. ```bash $ echo '[1,2,3]' | jq '.[-1]' 3 ``` ================================================ FILE: jq/reduce-object-to-just-entries-of-a-specific-type.md ================================================ # Reduce Object To Just Entries Of A Specific Type Let's say I have a large JSON data file with a ton of top-level fields of varying types. It can be hard to wade through it as is. [The `jq` utility](https://stedolan.github.io/jq/manual/) can help. I can filter a JSON object down to just the fields of a certain type. For instance, I may want to start with a view of the JSON that is restricted to just the values of type `array`. To do this, I need to use a couple different `jq` helper functions. ```bash jq '. | to_entries | map(select(.value | type | match("array"))) | from_entries' data.json ``` This starts with `to_entries` to convert an object into an array of key-value pairs. I then `map` over those pairs selecting just the pairs whose value matches the `"array"` type. I then use `from_entries` to turn the reduced array back into an object. There is a less verbose way to do the above. The `to_entries` and `from_entries` can be collapsed into a `with_entries` that wraps the `map` call. ```bash jq '. | with_entries(map(select(.value | type | match("array")))' data.json ``` ================================================ FILE: jq/turn-a-list-from-a-command-into-json.md ================================================ # Turn A List From A Command Into JSON There are a lot of command-line utilities that produce a list of things. Since JSON is a universal data format, it would be useful to be able to quickly turn some items from `stdout` into a JSON list. The [`jq`](https://jqlang.github.io/jq/) utility can help with this. Let's say I'm working with the following `git` command that lists changed files in a specific directory. ```bash $ git diff --name-only | grep some/dir ``` I can then pipe that list of files to `jq` with a few flags. ```bash $ git diff --name-only \ | grep some/dir \ | jq -R -s 'split("\n")[:-1]' ``` Here's what is going on: - The `-R` flag tells `jq` to accept raw input, rather than looking for JSON. - The `-s` flag is short for `--slurp` and tells `jq` to read in the entire input before applying the filter. - The string argument is the filter to be applied to the output. It splits on newlines and then takes the entire array except for the last item (`[:-1]`) which would be an empty string for the trailing newline. - `jq` automatically turns the whole thing into a formatted JSON list. ================================================ FILE: jq/zip-two-json-files-together-based-on-shared-id.md ================================================ # Zip Two JSON Files Together Based On Shared ID Let's say we have JSON file (`list1.json`) that contains an array of objects. Maybe they represent metadata about some books. Something like this: ```json [ { 'slug': 'the-subtle-knife', 'title': 'The Subtle Knife', ... }, { 'slug': 'all-systems-red', 'title': 'All Systems Red', ... }, { 'slug': 'piranesi-abc123', 'title': 'Piranesi', ... }, ... ] ``` And then we have another JSON file (`list2.json`) in a similar format that contains an additional piece of metadata tied to each slug: ```json [ { 'slug': 'the-subtle-knife', 'author': 'Philip Pullman' }, { 'slug': 'all-systems-red', 'author': 'Martha Wells' }, { 'slug': 'piranesi-abc123', 'author': 'Susanna Clarke' }, ... ] ``` And we want to pull the details from the second file and combine them into the first file based on that shared identifier, in this case, the `slug`. Instead of copying over a ton of values manually or writing a full-fledged script to do this, we can use a `jq` one-liner with the `--slurpfile` flag. ``` jq --slurpfile list1 list1.json --slurpfile list2 list2.json -n ' $list1[] as $item1 | $list2[] as $item2 | select($item1.slug == $item2.slug) | $item1 + $item2 ' [ { 'slug': 'the-subtle-knife', 'title': 'The Subtle Knife', 'author': 'Philip Pullman', ... }, { 'slug': 'all-systems-red', 'title': 'All Systems Red', 'author': 'Martha Wells', ... }, { 'slug': 'piranesi-abc123', 'title': 'Piranesi', 'author': 'Susanna Clarke', ... }, ... ] ``` This reads in both files as lists into named variables, selects for the items that have matching `slug` values, and then unions those objects together. The result will go to standard out, but it could also be redirected into a new JSON file. ================================================ FILE: kitty/set-the-title-of-a-window.md ================================================ # Set The Title Of A Window Kitty, I believe by default, will set the title of the current window to a previous run command. For instance, I often start out my terminal session by running a `tmux` command to create or join a tmux session. After this, the title of the window ends up sticking as something like `tmux new -s my-project`. Because I switch between various projects, I'd prefer the window title be something more generic. The window title can be manually set. To do this, open a 'New OS Window'—either from the menu or by hitting `Cmd-N`. Then run a `set-window-title` command with `kitty`. ```bash $ kitty @ set-window-title --match id:1 code ``` The `--match id:1` tells `kitty` what window to target with this command. Because I only ever keep one window open, the `id` of that window is always `1`. If you're not sure which window `id` to target, you can list the windows and find the one you are looking for. ```bash $ kitty ls ``` [source](https://sw.kovidgoyal.net/kitty/remote-control.html#kitty-set-window-title) ================================================ FILE: kitty/use-the-built-in-emoji-picker.md ================================================ # Use The Built-In Emoji Picker Kitty has a built-in emoji picker which you can use to search for and select an emoji character to be placed in your current terminal context. This is handy because Mac OSX's built-in emoji picker (Ctrl-Cmd-Space) doesn't work in Kitty. To open up Kitty's emoji picker, hit `Ctrl-Shift-u` (or prefix `u` with whatever your Kitty metakey is). You'll see a full screen menu with 4 different tabs. If you move to the _Emoji (F2)_ tab (`Ctrl-]` and `Ctrl-[` to navigate), you'll be able to search for an emoji based on its metadata name. For instance, if I type `check`, I'll see a bunch of unicode characters that match that term including `3 ✅ White heavy check mark`. By hitting tab until I reach that result, I can hit `Enter` to send that emoji to the terminal context. [source](https://sw.kovidgoyal.net/kitty/kittens/unicode-input.html) ================================================ FILE: linux/check-ubuntu-version.md ================================================ # Check Ubuntu Version Are you on Ubuntu? Want to know what version (release) of Ubuntu you are using? Run the following to find out: ```bash $ lsb_release -r ``` ================================================ FILE: linux/configure-your-server-timezone.md ================================================ # Configure Your Server Timezone On Ubuntu and Debian, you can open an interactive prompt for configuring your timezone with the following command: ```bash $ dpkg-reconfigure tzdata ``` ================================================ FILE: linux/list-the-statuses-of-all-upstart-jobs.md ================================================ # List The Statuses Of All Upstart Jobs To see a list of all known upstart jobs and their statuses, use the following command: ```bash $ initctl list ... console stop/waiting mounted-run stop/waiting acpid start/running, process 2927 checkfs.sh start/running checkroot-bootclean.sh start/running kmod stop/waiting mountnfs.sh start/running nginx stop/waiting plymouth-stop stop/waiting rcS stop/waiting ufw start/running ... ``` It will tell you for each job if it is stopped or started. See `man initctl` for more details. h/t Josh Davey ================================================ FILE: linux/show-current-system-time-and-settings.md ================================================ # Show Current System Time And Settings If you are accessing a remote server and viewing logs, you may want to know what the current system time is. The `timedatectl` command can show the current system date, time, and related settings. ```bash $ timedatectl Local time: Di 2015-04-07 16:26:56 CEST Universal time: Di 2015-04-07 14:26:56 UTC RTC time: Di 2015-04-07 14:26:56 Time zone: Europe/Berlin (CEST, +0200) Network time on: yes NTP synchronized: yes RTC in local TZ: no ``` This command can also be used to modify these settings. [source](http://man7.org/linux/man-pages/man1/timedatectl.1.html) ================================================ FILE: linux/show-used-and-available-system-memory.md ================================================ # Show Used And Available System Memory The `free` command will display the total RAM on your system as well as how much of it is used and how much is still freely available. ```bash $ free total used free shared buff/cache available Mem: 1000260 166004 552808 14440 281448 664724 Swap: 0 0 0 ``` The default output format of `free` is not very readable though. To make it more human readable, you can add the `-h` (_human_) and `--si` (_International System of Units_) flags. ```bash $ free -h --si total used free shared buff/cache available Mem: 976M 162M 547M 14M 266M 649M Swap: 0B 0B 0B ``` See `man free` for more details. ================================================ FILE: linux/upgrading-ubuntu.md ================================================ # Upgrading Ubuntu I recently discovered that my Linode box was running a fairly old version of Ubuntu. Because it is a remote box that I SSH into, there is no graphical user interface. Upgrading to a newer release can be accomplished with the following command line utility: ``` $ do-release-upgrade ``` It includes a series of prompts regarding choices about the upgrade and a lot of waiting. Adding the `-d` flag will upgrade to the latest development release. ================================================ FILE: llm/send-curl-to-claude-text-completion-api.md ================================================ # Send cURL To Claude Text Completion API Here is how we can make a `cURL` (`POST`) request to the Claude text completion API. It requires already having a Claude API account with (paid) credits. At this time, you can get $5 in free credits to try it out. Assuming all that, we can grab an API key, store it somewhere safe and accessible like 1Password, and then start formatting a request. We need to specify a couple headers as well as `POST` body parameters. ```bash curl -X POST \ -H "Content-Type: application/json" \ -H "x-api-key: $(op item get "Anthropic Claude API Key" --field credential)" \ -H "anthropic-version: 2023-06-01" \ -d \ '{ "model": "claude-2.1", "max_tokens_to_sample": 1024, "prompt": "Human: Show me an example of a simple Ruby program.\n\nAssistant:" }' \ https://api.anthropic.com/v1/complete ``` The required headers are: - `"Content-Type: application/json"` - `x-api-key` with our API key - `"anthropic-version: 2023-06-01"` (the latest Anthropic API version) Then, in the body, we specify: - the `model` (e.g. `claude-2.1`) - the max number of tokens you want the model to use - a prompt that starts with `Human:` and then prompts the `Assistant:` Note: this is a legacy API and the [Messages API](https://docs.anthropic.com/claude/reference/messages_post) should be preferred. [source](https://docs.anthropic.com/claude/reference/complete_post) ================================================ FILE: llm/use-the-llm-cli-with-claude-models.md ================================================ # Use The llm CLI With Claude Models [Simon Willison's `llm`](https://llm.datasette.io/en/stable/index.html) can be used with a bunch of different models (local and API). The whole thing is plugin driven. To use a specific model, you'll need to install the plugin for it. For instance, to use the [Claude 3 family of models](https://www.anthropic.com/news/claude-3-family) you need to install `llm-claude-3`. ```bash $ llm install llm-claude-3 ``` Then when prompting `llm`, specify which of the Claude models you want to use — `claude-3-haiku`, `claude-3-sonnet`, or `claude-3-opus` — with the `-m` flag: ```bash $ llm \ -m claude-3-haiku \ --key $CLAUDE_API_KEY \ 'Show me the SQL query to create a cocktails table.' ``` Note: instead of adding my Claude API key to the key store, I've opted to include it with the `--key` flag via an environment variable that I've set ahead of time. ================================================ FILE: mac/access-all-screen-and-video-capture-options.md ================================================ # Access All Screen And Video Capture Options There are a number of hotkeys to help you quickly capture a screenshot on the Mac. If you aren't sure which is which or what options are available, you can hit `Cmd+Shift+5`. This will open up a panel with the five main options. These include Capture Entire Screen, Capture Selected Window, and Capture Selected Portion for screenshots as well as Record Entire Screen and Record Selected Portion for videos. ================================================ FILE: mac/access-system-information-on-osx.md ================================================ # Access System Information OS X On machines running OS X, there is an Apple icon in the upper left corner as part of the menu bar. Clicking on this icon reveals a number of options. The first reads _About This Mac_. If you hold down the `option` key, however, that first option will instead read _System information..._ Select that option to access the System Information panel which can tell you details about your hardware, software, and network. h/t Dillon Hafer ================================================ FILE: mac/access-unsupported-screen-resolutions-with-rdm.md ================================================ # Access Unsupported Screen Resolutions With RDM If you visit the _Display Settings_ for your Mac, you'll find that you only have a handful of screen resolution options. For standard use, you'll get by with these. If you need a specific, unsupported resolution you'll need help from a 3rd party tool. There are many options out there. [RDM](https://github.com/avibrazil/RDM) is a free and open-source option. Once you have it installed and have given it _Accessibility_ permissions, open the menu from your top toolbar and select the resolution you are looking for. I use RDM to adjust my screen resolution to 1280x720 for optimal screencasting. ================================================ FILE: mac/add-a-bunch-of-cli-utilities-with-coreutils.md ================================================ # Add A Bunch Of CLI Utilities With coreutils The [`coreutils`](https://www.gnu.org/software/coreutils/) project is a collection of useful utilities that every operating system ought to have. > The GNU Core Utilities are the basic file, shell and text manipulation > utilities of the GNU operating system. These are the core utilities which are > expected to exist on every operating system. While many of these utilities are redundant with BSD utilities that MacOS chooses to ship with, there are some differences in the overlapping ons and then many additions from `coreutils`. They can be installed with Homebrew: ```bash $ brew install coreutils ``` And then you should have some new things available on your path. Take `shuf`, for instance. This utility can shuffle and select items from a file or incoming lines from another command. Here I use it to randomly grab a number between 1 and 5 (with the help of `seq`): ```bash ❯ seq 1 5 | shuf -n 1 3 ❯ seq 1 5 | shuf -n 1 2 ❯ seq 1 5 | shuf -n 1 5 ``` Or how about some utilities for manipulating file names? Among others there is `realpath`, `basename`, and `dirname`. ```bash ❯ realpath README.md /Users/lastword/dev/jbranchaud/til/README.md ❯ realpath README.md | xargs basename README.md ❯ realpath README.md | xargs dirname /Users/lastword/dev/jbranchaud/til ``` See the [manual](https://www.gnu.org/software/coreutils/manual/coreutils.html) for many more details. ================================================ FILE: mac/capture-screenshot-to-clipboard-from-cli.md ================================================ # Capture Screenshoot To Clipboard From CLI MacOS comes with a `screencapture` utility that you can run from the terminal to activate the built-in screenshot functionality on Mac. Usually when I am taking a screenshot, I want to do something with it right away. Such as paste it into an application or group chat. The `-c` flag forces the screen capture to go the clipboard. I also generally want to capture a specific area of the screen so that the captured image includes the right amount of context and nothing more. The `-i` flag puts you in interactive screen capture mode. That means your cursor will turn into a crosshair that you can use to make a drag selection of the capture area. ```bash $ screencapture -ic ``` Select an area to capture, it's now on your clipboard, paste it where you need it. Note: The first time you run this command, your terminal program (e.g. iTerm2) may prompt you for the necessary OS permissions in order to capture images of your screen. You'll need to grant those permissions and then rerun the command. See `man screencapture` for more details. ================================================ FILE: mac/check-network-quality-stats-from-the-command-line.md ================================================ # Check Network Quality Stats From The Command Line MacOS comes with a little known CLI utility for checking your current network quality statistics. It is aptly named `networkQuality`. Here is what a basic run of the tool might output: ```bash $ networkQuality ... Downlink: capacity 281.430 Mbps, responsiveness 101 RPM - Uplink: capacity 16.629 Mbps, responsiveness 101 R Downlink: capacity 285.534 Mbps, responsiveness 101 RPM - Uplink: capacity 16.028 Mbps, responsiveness 101 R ==== SUMMARY ==== Uplink capacity: 22.982 Mbps Downlink capacity: 288.152 Mbps Responsiveness: Low (93 RPM) Idle Latency: 26.375 milliseconds ``` You can get an even more detailed summary with the `-v` option: ```bash $ networkQuality -v ... ==== SUMMARY ==== Uplink capacity: 18.257 Mbps (Accuracy: High) Downlink capacity: 469.355 Mbps (Accuracy: High) Responsiveness: Medium (252 RPM) (Accuracy: High) Idle Latency: 25.583 milliseconds (Accuracy: High) Interface: en0 Uplink bytes transferred: 19.750 MB Downlink bytes transferred: 488.265 MB Uplink Flow count: 8 Downlink Flow count: 12 Start: 9/24/24, 11:06:20 AM End: 9/24/24, 11:06:30 AM OS Version: Version 13.5.2 (Build 22G91) ``` See `man networkQuality` for more details. [source](https://cyberhost.uk/the-hidden-macos-speedtest-tool-networkquality/) ================================================ FILE: mac/clean-up-old-homebrew-files.md ================================================ # Clean Up Old Homebrew Files If you've been using [Homebrew](https://github.com/Homebrew/homebrew) for a while, you may have built up some cruft in the form old and outdated files. These will not be cleaned up automatically. You have do tell Homebrew to do so. This can be done with the following command. ```bash $ brew cleanup ``` This command will report what files it cleans up as well as how much disk space it was able to clear. See `man brew` for more details. h/t Dorian Karter ================================================ FILE: mac/control-which-monitor-app-switcher-appears-on.md ================================================ # Control Which Monitor App Switcher Appears On For the most part when I hit `cmd+tab` (and `cmd+shift+tab`) to switch between apps, the visual switcher UI (which shows a row of the open apps) appears on my main monitor. However, sometimes I will be hitting `cmd+tab` and nothing shows up on my main monitor. I look to the right at my side monitor and there is the app switcher UI. Why is it appearing over there all of a sudden? The reason is that the app switcher UI is anchored to the same screen where the doc is located. Though the doc defaults to my main monitor, if I access the doc from the side monitor, now it is anchored there. To switch it back, I just have to make the doc slide up on my main monitor by running my mouse down to the bottom of that screen. The switch up was because I accidentally accessed the doc on my side monitor without realizing. [source](https://superuser.com/a/744680) ================================================ FILE: mac/convert-an-heic-image-file-to-jpg.md ================================================ # Convert An HEIC Image File To JPG I took a photo with my iPhone and then airdropped it to my Mac. This opened it up in Preview. From there I could see that the extension on the file was HEIC. I'm not familiar with that filetype and what I want is a JPG. The Preview app (which is already open in this scenario) can handle this conversion. Go to _File_ > _Export..._ Change the _format_ from `HEIC` to `JPEG`. Feel free to update the name and save location at this point as well. Then hit _Save_ and Preview will perform the conversion. [source](https://osxdaily.com/2019/11/22/convert-heic-to-jpg-mac-preview/) ================================================ FILE: mac/default-screenshot-location.md ================================================ # Default Screenshot Location By default, Mac saves all screenshots to the desktop. If you'd like screenshots to be dumped somewhere else, you have to configure it manually from the command line. For instance, if you'd like your screenshots to be saved in the `~/screenshots` directory, then enter the following commands: ```bash $ mkdir ~/screenshots $ defaults write com.apple.screencapture location ~/screenshots $ killall SystemUIServer ``` [source](http://osxdaily.com/2011/01/26/change-the-screenshot-save-file-location-in-mac-os-x/) ================================================ FILE: mac/detect-how-long-a-user-has-been-idle.md ================================================ # Detect How Long A User Has Been Idle The `ioreg` utility on MacOS dumps the I/O Kit registry tree. This lets us look at the state of all hardware devices and drivers registered with I/O Kit. Looking specifically at the Human Interface Device subsystem (`IOHIDSystem`), we can find a handful of properties including the `HIDIdleTime`. ```bash $ ioreg -c IOHIDSystem | awk '/HIDIdleTime/' | | | "HIDIdleTime" = 91831000 ``` That value is the number of nanoseconds since a human input device was last interacted with. That is the amount of time the user (me) has been idle. I can convert this to seconds, which is the small amount of time between me hitting enter in the terminal and the command finding the idle time. ```bash $ ioreg -c IOHIDSystem | awk '/HIDIdleTime/ {printf "%.2f seconds\n", $NF/1000000000}' 0.13 seconds ``` I can run this in `watch` to see the elapsed idle time increment. ```bash watch -n 1 "echo -n 'Idle time: '; ioreg -c IOHIDSystem | awk '/HIDIdleTime/ {printf \"%.1f seconds\\n\", \$NF/1000000000}'" ``` After watching the _idle time_ increment for a bit, I can move the mouse and watch it reset on the next `watch` loop. This could be used as part of a script that takes certain actions after the user has been idle for a while, like putting the display to sleep or stopping a time tracker app. There is a _lot_ going on in the `ioreg` output and it's hard to make sense of hardly any of it. I found running `ioreg -c IOHIDSystem | less`, searching for `IdleTime`, and browsing from there to be a good starting point. ================================================ FILE: mac/disable-swipe-navigation-for-a-specific-app.md ================================================ # Disable Swipe Navigation For A Specific App Mac's touch pad has a bunch of handy swipe gestures, including swiping two fingers to the left or the right to navigate backward or forward. This particular gesture can be globally enabled and disabled. I find it useful for most apps and a pain in a few apps, such as Google Chrome. From the terminal we can disable it for a specific app (like Google Chrome): ```bash $ defaults write com.google.Chrome AppleEnableSwipeNavigateWithScrolls -bool FALSE ``` Restart the target application, in my case Chrome. The left and right swipe navigation will no longer be triggered. [source](https://apple.stackexchange.com/questions/21236/how-do-i-disable-chromes-two-finger-back-forward-navigation) ================================================ FILE: mac/display-a-message-with-alfred.md ================================================ # Display A Message With Alfred Want to display some text in a large format on your screen for someone else to read? The [Alfred app](https://www.alfredapp.com/) can help. Activate Alfred and type in some text. Then hit `Cmd+L`. The shorter the snippet of the text, the larger it will appear. ================================================ FILE: mac/find-the-process-using-a-specific-port.md ================================================ # Find The Process Using A Specific Port The `netstat` utility is often recommended for finding the PID (process ID) bound to a specific port. Unfortunately, Mac's version of netstat does not support the `-p` (process) flag. Instead, you'll want to use the `lsof` utility. ```bash $ sudo lsof -i tcp:4567 ``` Running this will produce a nicely formatted response that tells you several pieces of information about the process bound to `:4567` including the PID. [source](https://stackoverflow.com/questions/3855127/find-and-kill-process-locking-port-3000-on-mac) ================================================ FILE: mac/gesture-for-viewing-all-windows-of-current-app.md ================================================ # Gesture For Viewing All Windows Of Current App When working on a web application, I often end up with a couple Chrome windows open and sometimes even a couple terminal windows open. I generally use the `Cmd+~` key binding to toggle through them. This can occasionally get confusing though. To get the fuller picture, I can get a birds-eye view of all windows for the current app using a track pad gesture. Swiping down with three fingers on the track pad will provide a zoomed out view of all windows. Click on the window you care about to view that or swipe back up with three fingers to dismiss it. [source](https://twitter.com/HipsterSmoothie/status/1084565618009862145) ================================================ FILE: mac/insert-a-non-breaking-space-character.md ================================================ # Insert A Non-Breaking Space Character On Mac, you can insert a non-breaking space character by hitting `option-space`. I'm not sure when people use non-breaking spaces, but this is a handy shortcut for those situations. [source](https://twitter.com/jnadeau/status/725436138601615360) ================================================ FILE: mac/inspect-assertions-preventing-sleep.md ================================================ # Inspect Assertions Preventing Sleep The `pmset` command is for inspecting and manipulating _Power Management Settings_ on MacOS. The `-g` flag is for _getting_ details. We can get a summary of power assertions with `-g assertions`. These assertions are ways that the system and display are prevented from sleeping. A common assertion preventing sleep is the user being active. Another example of an assertion is a program like `caffeinate` that sets a timeout preventing sleep for a fixed period of time. Here I activate a 30 minute (1600 second) `caffeinate` session and then I inspect the power management assertions which shows the details of that assertion as well as two others. ```bash ❯ caffeinate -t 1600 & [1] 98217 ❯ pmset -g assertions 2025-11-02 13:20:57 -0600 Assertion status system-wide: BackgroundTask 0 ApplePushServiceTask 0 UserIsActive 1 PreventUserIdleDisplaySleep 0 PreventSystemSleep 0 ExternalMedia 0 PreventUserIdleSystemSleep 1 NetworkClientActive 0 Listed by owning process: pid 98217(caffeinate): [0x00045477000194b3] 00:00:03 PreventUserIdleSystemSleep named: "caffeinate command-line tool" Details: caffeinate asserting for 1600 secs Localized=THE CAFFEINATE TOOL IS PREVENTING SLEEP. Timeout will fire in 1597 secs Action=TimeoutActionRelease pid 145(WindowServer): [0x00044f2f00099212] 00:00:00 UserIsActive named: "com.apple.iohideventsystem.queue.tickle serviceID:10009be9e service:AppleUserHIDEventService product:CTRL Keyboard eventType:3" Timeout will fire in 600 secs Action=TimeoutActionRelease pid 80(powerd): [0x00044f2f00019216] 00:22:34 PreventUserIdleSystemSleep named: "Powerd - Prevent sleep while display is on" ``` See `man pmset` and `man caffeinate` for more details. ================================================ FILE: mac/keyboard-shortcuts-for-interacting-with-text-areas.md ================================================ # Keyboard Shortcuts For Interacting With Text Areas When interacting with a document text area on MacOS (such as in the Notes app), there are a bunch of keyboard shortcuts made available to you via the operating system. A couple common ones that I'm used to from Unix environments are: - `ctrl-a` to move the cursor to the beginning of the line - `ctrl-e` to move the cursor to the end of the line - `ctrl-p` to move the cursor up a line (this is a common _previous_ keybinding) - `ctrl-n` to move the cursor down a line (this is a common _next_ keybinding) A handy one that I wasn't aware of is `ctrl-l` which will scroll the text area so that the cursor gets centered in the view area. A much more niche one I wasn't aware of is `ctrl-t` which swaps the character before the cursor with the character after the cursor. I can only imagine this being useful for quickly fixing transposed characters in a misspelled word, e.g. `baer`. There are many more *Document Shortcuts* as well as well as keyboard shorcuts for other apps and situations. The full listing is at [Mac Keyboard Shortcuts](https://support.apple.com/en-us/102650). ================================================ FILE: mac/launch-some-confetti.md ================================================ # Launch Some Confetti If you have [Raycast](https://www.raycast.com/) installed on your machine, then you have quick access to some confetti via their quick command palette. Trigger the command palette to open, start typing `confetti` until it appears as the focused option, and then hit enter. 🎉 We can launch confetti other ways, including programmatically from scripts. To do this, we need to first find the _deeplink_ for the Raycast _confetti_ program. Trigger the command palette and type out `confetti` again. However, this time instead of hitting enter, hit `Cmd+k` to open other actions. Find the _Copy Deeplink_ option. You should now have this on your clipboard: ``` raycast://extensions/raycast/raycast/confetti ``` With this deeplink in hand, we can now trigger confetti other places. The easiest way to do this is to open a terminal and pass that deep link as an argument to `open`. ```bash $ open raycast://extensions/raycast/raycast/confetti ``` Now you can wrap that up in any old bash script or even just tack it on to the end of a run of your test suite: ```bash $ rails test && open raycast://extensions/raycast/raycast/confetti ``` ================================================ FILE: mac/list-all-the-say-voices.md ================================================ # List All The Say Voices The `say` command can be a fun party trick. ```bash $ say Get ready for the bass to drop ``` Your friends will be even more impressed when you use some of the alternate voices. ```bash $ say -v Daniel "Would you like a cup of tea?" ``` To see all the alternate voices available, type the following ```bash $ say -v '?' ``` [source](http://stackoverflow.com/questions/1489800/getting-list-of-mac-text-to-speech-voices-programmatically) ================================================ FILE: mac/open-finder-app-to-specific-directory.md ================================================ # Open Finder.app To Specific Directory The Mac OSX built-in GUI for navigating directories and files is _Finder.app_. I use it just about anytime I need to drag a file on my machine into Slack, Discord, Google Drive, or wherever else has a file drop zone. Sometimes certain directories can be hard to navigate to because they are deeply nested or because Mac hides them by default. The `~/Library` directory is a great example of the latter. I can use the `open` command from the terminal to open a Finder window right in that directory. ```bash $ cd ~/Library $ open . ``` I first navigated there and then I ran `open .` which means open the current directory. For directories, `open` defaults to using Finder.app. I could have also run `open` with the target directory name instead of navigating there first. ```bash $ open ~/Library ``` See `man open` for more details. ================================================ FILE: mac/prevent-sleep-with-the-caffeinate-command.md ================================================ # Prevent Sleep With The Caffeinate Command MacOS has a built-in utility `caffeinate` that can programatically prevent your machine from sleeping. There are two kinds of sleep that it can prevent via _assertions_. > caffeinate creates assertions to alter system sleep behavior. The two kinds of sleep behavior are _display sleep_ and _system idle sleep_. An assertion to prevent display sleep can be created with `-d` and system idle sleep with `-i`. We can combine those to prevent both and then specify a duration (_timeout_) with `-t` (with a value in seconds). ```bash caffeinate -d -i -t 600 ``` This creates assertions with 10 minute timeouts for both display and system idle sleep. The `caffeinate` command is blocking, so if you want to start it in the background, you can do that like so: ```bash caffeinate -d -i -t 600 & ``` See `man caffeinate` for more details. ================================================ FILE: mac/quickly-type-en-dashes-and-em-dashes.md ================================================ # Quickly Type En Dashes And Em Dashes To type a hyphen, you hit the key right next to the zero: `-`. But what about if you need the similar looking en dash and em dash? Some programs have text areas that will automatically convert two consecutive dashes into an em dash. There is a more reliable method that can get you both: these two keybindings which are a variation of the `-` key itself. * `Option -` will produce an en dash (–), named that because it is typically the width of the n character. * `Option Shift -` will produce an em dash (—), named that because it is typically the width of the m character. The en dash seems to be the less familiar of the two. For more details on when to use that, [check out this excerpt from Dreyer's English](https://twitter.com/jbrancha/status/1344345683294380035). h/t [Shawn Wang (@swyx)](https://twitter.com/swyx/status/1344127570753646593?s=20) ================================================ FILE: mac/require-additional-js-libraries-in-postman.md ================================================ # Require Additional JS Libraries In Postman When writing pre-request scripts and test scripts as part of a [Postman](https://www.getpostman.com/) request, you aren't limited to vanilla JavaScript. There are a handful of libraries that can be required where needed. This short list of available libraries includes `cherrio`, `lodash`, and `moment`. To pull one of these into a particular script, use the standard `require` feature: ```javascript var moment = require('moment'); var now = moment(); ``` For a full list of what is available, check out [Postman's Sandbox API Reference](https://www.getpostman.com/docs/postman/scripts/postman_sandbox_api_reference). ================================================ FILE: mac/resize-app-windows-with-applescript.md ================================================ # Resize App Windows With AppleScript I showed in a [previous TIL](run-applescript-commands-inline-in-the-terminal.md) how we can run AppleScript commands inline from the terminal. Here is an inline command for positioning and resizing your iTerm2 window. ```bash osascript -e 'tell application "iTerm2" set the bounds of the first window to {50, 50, 1280, 720} end tell' ``` The first two values tell the command the `x` and `y` coordinates of where to position the upper left corner of the window relative to the upper left corner of your screen. The next two values are the `width` and `height` that the window should be resized to. [source](https://apple.stackexchange.com/questions/98064/set-size-of-window-to-exact-pixels-and-place-via-x-y-coordinates) ================================================ FILE: mac/resizing-both-corners-of-a-window.md ================================================ # Resizing Both Corners Of A Window Hold the `option` key while resizing a corner of a window and it will simultaneously and equivalently resize the opposite corner. ================================================ FILE: mac/reveal-location-of-file-in-finder-app.md ================================================ # Reveal Location Of File In Finder.app In the terminal I have the path to an image file. I want to open Finder.app to the location of that image file so that I can drag and drop it into a file upload area in the browser. Instead of opening a Finder.app window and navigating directory by directory to the location, I can use the `open` command. Using `open` directly with the image file will open the image in Preview.app. I want to reveal the directory that the image file is in within Finder.app. _Reveal_ is the keyword and the `-R` flag does just that. Here is an example of this that I actually ran when uploading a screenshot that went into [this blogmark post](https://still.visualmode.dev/blogmarks/255): ```bash $ open -R /Users/lastword/images/tiobe-index-graph-march-2026.png ``` See `man open` for more details. ================================================ FILE: mac/run-a-hardware-check.md ================================================ # Run A Hardware Check If your Mac is behaving in an odd way, there may be an issue with some piece of the hardware -- such as the RAM. You can perform a hardware check in order to chase down a diagnosis. - Shutdown your machine - Boot your machine - While it is booting, hold down the `d` key At this point, the machine should have booted into a special hardware check mode. Select your preferred language, the hardware check will be performed, and any issues will be reported. h/t Dillon Hafer ================================================ FILE: mac/run-applescript-commands-inline-in-the-terminal.md ================================================ # Run AppleScript Commands Inline In The Terminal [AppleScript](https://developer.apple.com/library/archive/documentation/AppleScript/Conceptual/AppleScriptLangGuide/introduction/ASLR_intro.html) is often invoked as a series of commands from a file. It is also possible to execute individual commands inline in the terminal. Use the `-e` flag to specify the command. For example, if you'd like to mute your Mac: ```bash $ osascript -e 'set volume 0' ``` Run that and your Mac's volume will now be at zero. [source](http://osxdaily.com/2016/08/19/run-applescript-command-line-macos-osascript/) ================================================ FILE: mac/set-a-window-to-its-default-zoom-level.md ================================================ # Set A Window To Its Default Zoom Level Often when showing my screen to someone else or connecting to a project, I have to adjust the zoom level of my current window by hitting `Cmd +` a bunch of times. Once I am done I usually do some guess work to get the screen size back to what I am used to, hitting `Cmd -` a couple times. There is an easier way. Hitting `Cmd 0` will return the window back to its default zoom level. h/t Jake Worth ================================================ FILE: mac/specify-app-when-opening-from-command-line.md ================================================ # Specify App When Opening From Command Line When you `open` files from the command line ```bash $ open README.md ``` the default app for that filetype will be used. For me, `README.md` will be opened in VS Code. If you have a different app in mind, you can specify it using the `-a` flag: ```bash $ open README.md -a Notes ``` This will open `README.md` in Mac's Notes app. See `man open` for more details. ================================================ FILE: mac/start-amphetamine-session-with-applescript.md ================================================ # Start Amphetamine Session With AppleScript I use the _Amphetamine_ app on Mac to keep my computer from going to sleep during the day. It is a menu bar app that can be used to start a _Session_ of time where it will keep your computer from going to sleep. At the start of my day, I'll typically start an 8 hour _Session_. This is useful if I have to step away fo 10 minutes or if I'm doing some writing in my notebook, my computer won't go to sleep on me. Though these sessions can be controlled from the menu bar app, I was excited to learn that I can also programatically start a session with AppleScript. Here is how to start a _Session_ (overriding an existing session) with options that specify it is 8 hours long and the display should not be allowed to sleep. ```bash $ osascript -e 'tell application "Amphetamine" to start new session with options {duration:8, interval:hours, displaySleepAllowed:false}' ``` The `interval` could also be `minutes` and then I could change the duration to an amount of time that makes sense in minutes, e.g. `90` for 1.5 hours. Note: the `with options {...}` segement is all or nothing. All three need to be included or don't include the clause at all. Additionally, a session of indefinite duration can be started by including no options: ```bash $ osascript -e 'tell application "Amphetamine" to start new session' ``` And any existing session can be ended with: ```bash $ osascript -e 'tell application "Amphetamine" to end session' ``` [source](https://iffy.freshdesk.com/support/solutions/articles/48000078223-applescript-documentation) ================================================ FILE: mac/uninstall-logitech-g-hub-from-mac.md ================================================ # Uninstall LogiTech G Hub From Mac I rarely uninstall software from my Mac. And unless the software is nice enough to provide a clear 'Uninstall' flow, it is not straightforward how to do it. In fact, it probably varies quite a bit from app to app. In the case of LogiTech's G Hub, I was able to find the following instructions for uninstalling it. The thing of note is that the updater app can take an `--uninstall` flag. ```bash sudo /Applications/lghub.app/Contents/MacOS/lghub_updater.app/Contents/MacOS/lghub_updater --uninstall ``` I still had to remove the app launcher from my `Applications` directory. [source](https://www.reddit.com/r/LogitechG/comments/bluth5/comment/lbhctx1/) ================================================ FILE: mac/use-a-different-font-with-iterm2.md ================================================ # Use A Different Font With iTerm2 I wanted to give [`gh-dash`](https://github.com/dlvhdr/gh-dash) a try, but after installing and opening it up, I was seeing a bunch of `?` characters where specialized font icons were missing. Their README recommended installing a [`Nerd Font`](https://github.com/ryanoasis/nerd-fonts) that includes those icons, such as [`Fira Code`](https://github.com/tonsky/FiraCode). I was able to install `font-fira-code-nerd-font` with homebrew: ```bash $ brew install font-fira-code-nerd-font ``` Then to get iTerm2 to start using that font, I had to change the font setting for my current profile. Under the _iTerm2_ menu is _Settings..._. From there, I clicked the _Profiles_ section. For the _Default_ profile, I went to the _Text_ tab and under _Font_ I selected _FireCode Nerd Font Mono_ from the dropdown. That won't take effect on any current iTerm2 windows. Since I have everything running through `tmux`, I could close my current window, open a new one (`Cmd+N`), and reconnect to my existing `tmux` session. Now when I run `gh dash`, I see all the font icons that were missing before. ================================================ FILE: mac/use-default-screenshot-shortcuts-with-cleanshot-x.md ================================================ # Use Default Screenshot Shortcuts With CleanShot X _[Watch the screencast](https://www.youtube.com/watch?v=yoMWfe1F9h4) that accompanies this TIL to see how to do this visually._ On macOS there are a set of keyboard shortcuts for taking screenshots. The one that I use most often is `Cmd-Shift-4`. It turns the cursor into cross hairs so that I can make a drag selection of part of the screen to be captured as a screenshot. I've recently transitioned to using CleanShot X for taking screenshots. It offers some improvements over the built-in screenshot tool, like annotations and sticking recently screenshots in the corner of the window. As part of this transition, I wanted `Cmd-Shift-4` to activate CleanShot X's _Capture Area_ feature instead of the built-in one. To do this, first open up _System Preferences > Keyboard > Shortcuts_. Then go to the _Screenshots_ section and de-select any shortcuts that you want to remap. For `Cmd-Shift-4`, that is "Save picture of selected area as a file". Then I open _Preferences > Shortcuts_ for CleanShot X and map _Capture Area_ to `Cmd-Shift-4`. Hitting `Cmd-Shift-4` will now activate CleanShot X's Capture Area instead of the one for the built-in screenshot app. ================================================ FILE: mac/view-all-windows-of-the-current-app.md ================================================ # View All Windows Of The Current App In [Gesture For Viewing All Windows Of Current App](gesture-for-viewing-all-windows-of-current-app.md), I showed off a track pad gesture that allows you to view all window instances of the app you are currently focused on. There is an analog keyboard shortcut that allows you to do the same thing -- without dragging your hands away from the keys. Tap `Ctrl+↓` to do the same. ================================================ FILE: mac/write-system-clipboard-to-a-file.md ================================================ # Write System Clipboard To A File MacOS has two CLI utilities `pbcopy` and `pbpaste` which, respectively, copy _to_ and paste _from_ the system clipboard via the CLI. Let's say I've just copied a large block of text from somewhere onto my system clipboard. I now want to paste that into a new file. Instead of creating a new file, opening it up in my preferred editor, pasting all that text, and saving the file, I can run one small command from the CLI. ```bash $ pbpaste > data.txt ``` This redirects the contents of `pbpaste` (which is the system clipboard) into the file `data.txt`. If that file doesn't already exist, then it will be created before the data is written to it. See `man pbpaste` for more details. ================================================ FILE: mise/create-umbrella-task-for-all-test-tasks.md ================================================ # Create Umbrella Task For All Test Tasks When I was first sketching out the [`mise` tasks](https://mise.jdx.dev/tasks/running-tasks.html) for a Rails app, I added the following two tasks. One is for running all the `rspec` tests. The other is for running all the `vitest` (JavaScript) tests. ```toml [tasks."test:rspec"] run = "unbuffer bundle exec rspec" description = "Run RSpec tests" depends = ["bundle-install"] [tasks."test:vitest"] run = "unbuffer yarn test run" description = "Run Vitest tests" depends = ["node-install"] ``` I didn't want to have to invoked both of this individually every time I wanted to run the full suite. So I added a `test:all` task to do it all. ```toml [tasks."test:all"] description = "Run all tests (RSpec and Vitest)" run = [ "unbuffer bundle exec rspec", "unbuffer yarn test run", ] description = "Run RSpec tests" depends = ["bundle-install", "node-install"] ``` This worked (for now). But it ate at me, for a couple reasons. I had to duplicate everything about the existing `test:rspec` and `test:vitest` tasks. And this didn't account for a new kind of test task being added (e.g. `test:e2e`). Instead, I can rely on `depends` and wildcards to achieve this without the duplication which makes it more future-proof. ```toml [tasks."test:all"] description = "Run all tests (RSpec and Vitest)" depends = ["test:*"] ``` Running `mise run test:all` won't execute its own command, but because it depends on all other `test:*` tasks, the tests will get run through those dependencies. This task naming pattern also allows for calling all tests with `mise run "test:**"`. ================================================ FILE: mise/list-the-files-being-loaded-by-mise.md ================================================ # List The Files Being Loaded By Mise While running `mise` for the first time, after adding a `mise.toml` file to a project, I noticed something strange. Instead of invoking the command I had specified (`mise run dev`), several parellel tool downloads were kicked off. In addition to Ruby, it was installing an older version of Postgres, and lua. What gives? By running `mise cfg`, I can list all the files being loaded by `mise` and get to the bottom of this. ```bash mise cfg Path Tools ~/.tool-versions node, ruby, postgres, lua ~/code/still/.ruby-version ruby ~/code/still/Gemfile (none) ~/code/still/.tool-versions ruby ~/code/still/mise.toml (none) ``` I was only thinking about the files local to my project and I forgot that I have a system-wide `.tool-versions` file. As we can see from the output, that file specifies `postgres` and `lua` as well. Mise wanted to ensure that it had downloaded the specified versions of each of those tools before running my task. [source](https://mise.jdx.dev/configuration.html) ================================================ FILE: mise/look-in-ruby-version-dotfile.md ================================================ # Look In Ruby Version Dotfile Newer versions of [`mise`](https://mise.jdx.dev/dev-tools/) specifically only look for tool versions in `mise.toml` as well as the asdf `.tool-versions` file. A lot of Ruby projects use the `.ruby-version` file to indicate the Ruby version of a project. To continue to use the `.ruby-version` file instead of migrating to `mise.toml`, you need to tell `mise` that you prefer to use the idiomatic version file. I added the following line to my [`~/.config/mise/config.toml`](https://github.com/jbranchaud/dotfiles/commit/8edeb7a9c53500e89e88b4079cbd1859ebebcbda) file: ```toml idiomatic_version_file_enable_tools = ["ruby"] ``` Now, whenever `mise` is looking for the specified Ruby version of a project, it will also look for `.ruby-version`. Here is a [full list of idomatic version files supported by `mise`](https://mise.jdx.dev/configuration.html#idiomatic-version-files). See [`idiomatic_version_file_enable_tools`](https://mise.jdx.dev/configuration/settings.html#idiomatic_version_file_enable_tools) as well as the [Ruby-specific documentation](https://mise.jdx.dev/lang/ruby.html#ruby-version-and-gemfile-support) for more details. ================================================ FILE: mise/override-your-project-mise-file.md ================================================ # Override Your Project Mise File A project I'm working on has a version-controlled `.mise.toml` file in it. Some changes were made to that recently that introduce some env vars that conflict with my setup. If I make edits to that file, then I have a modified version of `.mise.toml` sitting in my Git working copy. ``` # .mise.toml [env] CONFIG_SETTING = "project" ``` Instead, I can rely on the loading precedence rules of `mise` to override those project settings with my individual settings. I can do that with the `.mise.local.toml` file which is played on top of any `mise` configuration from files further down the precedence chain. ``` # .mise.local.toml [env] CONFIG_SETTING = "override" ``` Assuming I have `mise` setup with my shell environment to automatically load in these files, I can now check what takes precedence: ```bash $ echo $CONFIG_SETTING override ``` Make sure `.mise.local.toml` is included in the `.gitignore` file to avoid checking in your personal environment overrides. To be sure about what files are loaded and in what order, give `mise cfg` a try. I discuss that in more detail in [List The Files Being Loaded By Mise](list-the-files-being-loaded-by-mise.md). ================================================ FILE: mise/pick-from-tasks-using-interactive-picker.md ================================================ # Pick From Tasks Using Interactive Picker In [Add Mise Tasks For Common Workflow Commands](https://www.visualmode.dev/add-mise-tasks-for-common-workflow-commands), I wrote about a set of tasks I added as shortcuts for connecting to the `rails console` in various environments. ```toml # mise.toml [tasks."console:staging"] description = "Open a Rails console on staging" run = "ssh -t my-app-staging dokku run my-app rails console" [tasks."console:prod"] description = "Open a Rails console on production" run = "ssh -t my-app-prod dokku run my-app rails console" ``` When a project is configured with multiple `mise` tasks like this, we can invoke `mise run` without any specific arguments and it will prompt you with an interactive picker. The picker will populate with all the tasks like so: ```bash ❯ mise run Tasks Select a task to run ❯ console:prod Open a Rails console on production console:staging Open a Rails console on staging / esc clear filter • enter confirm ``` We can navigate between the options with the arrow keys (and if we exit _filter_ mode by hitting `esc`, then `j/k` also work to move down and up). While in _filter_ mode, we can type into the prompt which will filter the list of commands down to just the partial matches. Once we're targeting the task we want to run, we hit `enter` and the task is executed. ================================================ FILE: mise/preserve-color-output-for-task-command.md ================================================ # Preserve Color Output For Task Command I decided to wrap a couple test running commands for a project into a single `test:all` mise task. It looked something like this: ```toml [tasks."test:all"] run = """ bundle exec rspec yarn test run """ description = "Run all tests (RSpec and Vitest)" depends = ["bundle-install", "node-install"] ``` I can run this with `mise run test:all` and it works. However, there is a glaring issue that immediately juts out. All of the test runner output is uncolored text. I'm used to and strongly prefer greens (passes), reds (fails), and yellows (skips) of test runner output. The test runners lose the text coloring when run through `mise` because they believe they are not running in _interactive_ mode. The [`expect`](https://linux.die.net/man/1/expect) tools (`brew install expect`) install with another binary called [`unbuffer`](https://linux.die.net/man/1/unbuffer). `unbuffer` can coerce a command to run in interactive mode. Prepending these test runner commands with `unbuffer` will preserve the colors as the results are output to the terminal. Here is the update `test:all` task: ```toml [tasks."test:all"] run = """ unbuffer bundle exec rspec unbuffer yarn test run """ description = "Run all tests (RSpec and Vitest)" depends = ["bundle-install", "node-install"] ``` For some commands, it seems able to stream out (rather than _buffer_) the results (e.g. with `vitest`). Whereas with `rspec`, the test suite runs to completion and is then output to the terminal. I'm still investigating streaming the `rspec` results. ================================================ FILE: mise/read-existing-dot-env-file-into-env-vars.md ================================================ # Read Existing Dot Env File Into Env Vars Just about any web app that I've worked on has had a `.env` file as a way of configuring aspects of the app specific to that environment. These typically are read into the environment with a language-specific [dotenv](https://github.com/bkeepers/dotenv) tool. Mise supports this convention. In addition to specifying individual non-secret env vars, you can also instruct `mise` to read-in a `.env` file like so: ```toml [env] PORT=3344 _.file = ".env" ``` The `_.file` line tells `mise` that there is a file `.env` with key-value pairs that it should read in. It can even handle `.env.json` and `.env.toml` file formats. To ensure that `mise` is picking up the values from the `.env` file, you can run the following command and make sure they show up in the output: ```bash $ mise env ``` [source](https://mise.jdx.dev/environments/secrets.html) ================================================ FILE: mise/run-a-command-with-specific-tool-version.md ================================================ # Run A Command With Specific Tool Version Because I'm using `mise` to manage the versions of tools like Node, I can execute commands in the context of specific versions. Behind the scenes `mise` makes sure I have the necessary tool(s) installed at the desired version(s). So, [`mise exec` command](https://mise.jdx.dev/cli/exec.html) will default to using the latest version of a tool if I haven't been more specific. At the time of this writing, for Node, that is v23. ```bash $ mise exec node -- node --version v23.9.0 ``` To be specific I could specify the major version with `node@23` like so: ```bash mise exec node@23 -- npx repomix Need to install the following packages: repomix@0.2.39 Ok to proceed? (y) y ... ``` Or if I wanted to use a different, older version of Node, I could specify that as well. We can see it will first install that and then execute the command: ```bash $ mise exec node@22 -- npx repomix gpg: Signature made Tue Feb 11 04:44:53 2025 CST gpg: using RSA key C0D6248439F1D5604AAFFB4021D900FFDB233756 gpg: Good signature from "Antoine du Hamel " [unknown] 📦 Repomix v0.2.39 ... ``` ================================================ FILE: mise/search-through-bin-paths-for-tool-locations.md ================================================ # Search Through Bin Paths For Tool Locations The `mise bin-paths` command will list all the bin paths that are managed by `mise`. When you tell `mise` to install a tool, it installs a specific version at a location where its binaries can be made accessible on the system path. While `mise ls` is useful for seeing what is installed by `mise` and at what version, the `bin-paths` command can tell you where those tool installations with their binaries are located. Combine this with `grep` or `rg` to narrow down the results to tools by a specific name: ```bash ❯ mise bin-paths | rg 'neovim' /Users/lastword/.local/share/mise/installs/npm-neovim/5.4.0/bin /Users/lastword/.local/share/mise/installs/pipx-neovim-remote/2.5.1/bin /Users/lastword/.local/share/mise/installs/neovim/0.11.6/bin ``` I can then look in one of these directories to see the one or more binaries that they include. For instance, here is what is in the `node` bin path: ```bash ❯ ls /Users/lastword/.local/share/mise/installs/node/22.22.0/bin  ./  ../  claude@  corepack@  node*  npm*  npx@ ``` See `mise bin-paths --help` for more details. ================================================ FILE: mongodb/determine-the-database-version.md ================================================ # Determine The Database Version Whether your Mongo database is local or remote, you should connect to it using the [`mongo` CLI](https://docs.mongodb.com/manual/mongo/). Once connected, you can issue the following query: ``` db.version() ``` This will output the version of your Mongo database. [source](https://docs.mongodb.com/manual/reference/method/db.version/) ================================================ FILE: mongodb/dump-a-remote-database.md ================================================ # Dump A Remote Database Here is a single-line script for dumping a remote mongo database. This will dump it to your local filesystem in a binary format (`.bson`) that you can then restore to another mongo instance. Create a file like this called `mongdump-cmd` and replace the various placeholder values (e.g. ``) with valid values for your remote mongo instance. ```bash mongodump --host ':' \ --ssl \ --username '' \ --password '' \ --authenticationDatabase 'admin' \ --out ./mongo-backups/-dump ``` Create the backup directory: ```bash $ mkdir mongo-backups ``` Then execute the bash script: ```bash $ bash mongodump-cmd ``` This will dump everything on the remote instance into the `mongo-backups/2020-06-20-dump/` directory. You can also include the `--db` flag to dump a specific database. This can later be used with `mongoresetore` to restore the data to the mongo instance you specify. ================================================ FILE: mongodb/dump-and-restore-with-a-single-gzip-file.md ================================================ # Dump And Restore With A Single gzip File The `mongodump` and `mongorestore` utilities provide a way for grabbing all the data from one database and putting it into another database. These commands are useful for transitioning production data to a database instance with more computing resources. The `--archive` and `--gzip` flags, supported by both commands, are what allow us to do the whole process with a single file. Without flags, `mongodump` will output multiple `.bson` files. Here is what the `mongodump` command might look like pointed at a remote URI: ```bash mongodump \ --uri="mongodb+srv://:@" \ --archive="myapp-dump.20221105.gz" \ --gzip ``` This will take a little while to run based on the size of the database. The result will be a file in your current directory with the name `myapp-dump.20221105.gz`. Because it is gzip'd, it will be a few times smaller than the standing database. To then load all the data into your new Mongo database cluster, you'll use `mongorestore` with all the same flags, making sure to swap out the destination URI details with those of the new instance. ```bash mongorestore \ --uri="mongodb+srv://:@" \ --archive="myapp-dump.20221105.gz" \ --gzip ``` For more details, see [Output an Archive File](https://www.mongodb.com/docs/database-tools/mongodump/#output-to-an-archive-file) and [Compress the Output](https://www.mongodb.com/docs/database-tools/mongodump/#compress-the-output). ================================================ FILE: mongodb/get-size-stats-for-a-collection.md ================================================ # Get Size Stats For A Collection For any collection in your MongoDB instance. ```javascript > db.getCollectionNames() ["books", "authors", "genres"] ``` You can list a collection of stats, which include the amount of disk space that collection is utilizing. ```javascript > db.books.stats().size 11057056 ``` By default this size is in bytes, which isn't all that human-readable of a value. By passing in a `scale` value to `stats()`, you can get a value that is a bit more understandable. A scale of `1024` would give you kilobytes, so a scale of `1024 * 1024` would give you megabytes. ```javascript > db.books.stats({ scale: 1024 * 1024 }).size 10 ``` [source](https://docs.mongodb.com/manual/reference/method/db.collection.stats/) ================================================ FILE: mongodb/list-size-stats-for-all-collections.md ================================================ # List Size Stats For All Collections In [Get Size Stats for a Collection](get-size-stats-for-a-collection.md), we saw how to use `db.collection.stats()` and its `scale` parameter to get a useful size metric for a given collection. We can combine some of this concepts with some scripting to list human-readable size metrics for all of our database's collections: ```javascript > db.getCollectionNames().forEach(function (collectionName) { sizeInMb = db[collectionName].stats({ scale: 1024 * 1024 }).size; print(collectionName + ": " + sizeInMb + "MB"); }) books: 10MB authors: 2MB genres: 1MB ``` This snippet gets all the collections for the current database and iterates over them. For each collection name, it looks up the `size` stat for that collection scaled to megabytes and then prints it all out with some contextual information. [source](https://docs.mongodb.com/manual/faq/storage/#data-storage-diagnostics) ================================================ FILE: mysql/change-existing-column-to-not-null.md ================================================ # Change Existing Column To Not Null Let's say you have an existing nullable column. You want to update the schema to enforce a `not null` constraint on that column. You can do that with an [`alter table`](https://dev.mysql.com/doc/refman/8.0/en/alter-table.html) DDL statement. You can do this with the `modify` or `change` option. With `modify` you redeclare the column definition with the options that you want. You'll need to know and specify the existing data type of that column. ```sql alter table books modify publication_year int not null; ``` It is possible, but clumsy to do this with the `change` option because you declare the column name twice. That's because `change` is typically used to rename a column. ```sql alter table books change publication_year publication_year int not null; ``` If you're updating a column for a table that already contains data, make a plan to backfill any existing records that have `null` for that column. Every record will need a value in that column before the `modify` can be applied. [source](https://stackoverflow.com/a/6305252/535590) ================================================ FILE: mysql/connect-to-a-database-in-safe-update-mode.md ================================================ # Connect To A Database In Safe Update Mode The MySQL client has a _Safe-Updates Mode_ that you can use when connecting to a database. When this mode is active, the client will interrupt `update` and `delete` commands that don't specify a `where` clause that filters by a _key_ value. That, or you need to explicitly `limit` the number of rows impacted by the query. To start a connection in this mode, you can use either the `--safe-updates` flag or the cheekier `--i-am-a-dummy` flag. ```bash $ mysql --i-am-a-dummy -h ::1 -P 3309 -u root -D my-database ``` Then if you try to do an unrestricted `update` or `delete`, you'll see the following message: ```sql mysql> update users set email = 'oops@email.com'; ERROR 1175 (HY000): You are using safe update mode and you tried to update a table without a WHERE that uses a KEY column. ``` This can also be set within the connection like so: ```sql mysql> set sql_safe_updates=1; ``` [source](https://dev.mysql.com/doc/refman/8.0/en/mysql-tips.html#safe-updates) ================================================ FILE: mysql/default-username-and-password-for-new-instance.md ================================================ # Default Username And Password For New Instance Let's say you've set up a fresh new instance of MySQL. Perhaps in a docker container with a MySQL image. When you first connect to the instance, you'll be prompted for a username and password. What is the username and password for a MySQL instance you've just created? There are defaults. The default username is `root` and the default password is left blank. So, your connection URL will look something like this: ``` mysql://root@localhost:3306 ``` You can use that on the CLI or plug it in to the connection details panel of your favorite SQL client. [source](https://dev.mysql.com/doc/refman/8.0/en/default-privileges.html) ================================================ FILE: mysql/display-output-in-a-vertical-format.md ================================================ # Display Output In A Vertical Format Output for tables with lots of columns can be hard to read and sometimes overflow the terminal window. Consider the output from [Show Indexes For A Table](show-indexes-for-a-table.md): ```sql > show indexes in users; +-------+------------+--------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+ | Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment | +-------+------------+--------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+ | users | 0 | PRIMARY | 1 | id | A | 0 | NULL | NULL | | BTREE | | | | users | 0 | unique_email | 1 | email | A | 0 | NULL | NULL | | BTREE | | | +-------+------------+--------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+ ``` We can vertically orient the output of a statement by terminating it with `\G` instead of `;` (or `\g`). ```sql > show indexes in users\G *************************** 1. row *************************** Table: users Non_unique: 0 Key_name: PRIMARY Seq_in_index: 1 Column_name: id Collation: A Cardinality: 0 Sub_part: NULL Packed: NULL Null: Index_type: BTREE Comment: Index_comment: *************************** 2. row *************************** Table: users Non_unique: 0 Key_name: unique_email Seq_in_index: 1 Column_name: email Collation: A Cardinality: 0 Sub_part: NULL Packed: NULL Null: Index_type: BTREE Comment: Index_comment: ``` ================================================ FILE: mysql/doing-date-math.md ================================================ # Doing Date Math MySQL has an array of functions for interacting with `date` and `datetime` values. If you'd like to do math with a date to compute a date in the future or the past, you can use the [`DATE_ADD()`](https://dev.mysql.com/doc/refman/5.5/en/date-and-time-functions.html#function_date-add) and [`DATE_SUB()`](https://dev.mysql.com/doc/refman/5.5/en/date-and-time-functions.html#function_date-sub) functions. ```mysql mysql> select now() Now, date_add(now(), interval 10 minute) '10 Minutes Later'; +---------------------+---------------------+ | Now | 10 Minutes Later | +---------------------+---------------------+ | 2018-10-18 15:53:29 | 2018-10-18 16:03:29 | +---------------------+---------------------+ mysql> select now() Now, date_sub(now(), interval 9 day) '9 Days Earlier'; +---------------------+---------------------+ | Now | 9 Days Earlier | +---------------------+---------------------+ | 2018-10-18 15:54:01 | 2018-10-09 15:54:01 | +---------------------+---------------------+ ``` There are equivalent `ADDDATE()` and `SUBDATE()` functions if you prefer. Check out [the docs](https://dev.mysql.com/doc/refman/5.5/en/date-and-time-functions.html) for more details. ================================================ FILE: mysql/dump-a-database-to-a-file.md ================================================ # Dump A Database To A File The `mysqldump` client is a handy tool for creating a backup or snapshot of a MySQL database. The standard use of this command produces an alphabetical series of statements that comprise the structure and data of the specified database. It directs all of this to `stdout`. You'll likely want to redirect it to a file. ```bash $ mysqldump my_database > my_database_backup.sql ``` The output will include special comments with MySQL directives that disable things like constraint checking. This is what allows the output to be in alphabetical order without necessarily violating any foreign key constraints. If you need to dump multiple databases, include the `--databases` flag with a space-separated list of database names. Or dump all of them with `--all-databases`. See `man mysqldump` for more details. ================================================ FILE: mysql/echo-a-message-from-a-sql-file.md ================================================ # Echo A Message From A SQL File Let's say we have a SQL file that we run to seed our database. We want to echo a message to stdout at the beginning of that file's execution. We can do this with [a MySQL client _shell command_](https://dev.mysql.com/doc/mysql-shell/8.0/en/mysql-shell-commands.html). Specifically, we need to use the `\system` or `\!` command to run our system's `echo` command. Here is what that could look like: ```sql \! echo '*****************************************' \! echo '* *' \! echo '* Loading seed data into the database *' \! echo '* *' \! echo '*****************************************' insert into products ... ``` That message banner will be output when you run the script. ```bash $ mysql -h ::1 -P 3306 -u root -D local_database < seed_data.sql ***************************************** * * * Loading seed data into the database * * * ***************************************** ``` ================================================ FILE: mysql/get-idea-of-what-is-in-a-json-column.md ================================================ # Get Idea Of What Is In A JSON Column While digging through some data trying to reacquaint myself with the overall schema and data model, I ran into an issue selecting rows from this `content_resource` table. There was so much text packed in to the `"body"`, `"summary"`, and `"description"` key-value pairs of `fields` JSON column that a simple `select * ... limit 3;` was overwhelming the screen with text and table formatting characters (i.e. `+------+-------`). I figured the `fields` JSON followed a reliable structure, at least for records of the same `type`. So, let's start by only grabbing the [`json_keys`](https://dev.mysql.com/doc/refman/8.4/en/json-search-functions.html#function_json-keys) so that I can get a sense of the shape of the JSON. ```sql select id, json_keys(fields) from content_resource where type = 'post' limit 3; +-----+-----------------------------------------------------------------------------------------------------------+ | id | json_keys(`fields`) | +-----+-----------------------------------------------------------------------------------------------------------+ | 1 | ["body", "slug", "state", "title", "summary", "postType", "visibility", "description", "originalLessonId"] | | 2 | ["body", "slug", "state", "title", "summary", "postType", "visibility", "description", "originalLessonId"] | | 3 | ["body", "slug", "state", "title", "summary", "postType", "visibility", "description", "originalLessonId"] | +-----+-----------------------------------------------------------------------------------------------------------+ ``` For the `post` type, I see the same keys for this sampling of rows. Now I have an idea what keys are present and can start digging in further. My next query might look something like this: ```sql select id, fields->'$.slug', fields->'$.title', fields->'$.state' from content_resource where type = 'post' limit 3; ``` ================================================ FILE: mysql/ignore-duplicates-when-inserting-records.md ================================================ # Ignore Duplicates When Inserting Records While trying to run a seed script to set up some application data in a MySQL database, I ran into several duplicate-key errors. Some of this data had already been added in another context, but I still need some of the seeds. ```sql insert into MerchantAccount (col1, col2, col3) values ('data1', 'data2', 'data3'), ('data4', 'data5', 'data6'), (...); ``` The solution was to allow MySQL to `ignore` the duplicate records and insert the rest. ```sql insert ignore into MerchantAccount (col1, col2, col3) values ('data1', 'data2', 'data3'), ('data4', 'data5', 'data6'), (...); ``` Notice all I had to do was update the statment by adding `ignore` right after [`insert`](https://dev.mysql.com/doc/refman/8.0/en/insert.html). > If you use the `IGNORE` modifier, ignorable errors that occur while executing > the `INSERT` statement are ignored. For example, without `IGNORE`, a row that > duplicates an existing `UNIQUE` index or `PRIMARY KEY` value in the table > causes a duplicate-key error and the statement is aborted. With `IGNORE`, the > row is discarded and no error occurs. Ignored errors generate warnings > instead. [source](https://dev.mysql.com/doc/refman/8.0/en/sql-mode.html#ignore-effect-on-execution) ================================================ FILE: mysql/list-databases-and-tables.md ================================================ # List Databases And Tables If you've started a [mysql](https://dev.mysql.com/) session, but haven't connected to a particular database yet, you can list the available databases like so: ```sql > show databases; +-----------------------------+ | Database | +-----------------------------+ | information_schema | | my_app_dev | +-----------------------------+ ``` If you are curious about the tables in a particular database, you can list them by specifying the database's name: ```sql > show tables in my_app_dev; +------------------------------+ | Tables_in_my_app_dev | +------------------------------+ | pokemons | | trainers | +------------------------------+ ``` Alternatively, you can connect to the database of interest and then there is no need to specify the name of the database going forward. ```sql > use my_app_dev; > show tables; +------------------------------+ | Tables_in_my_app_dev | +------------------------------+ | pokemons | | trainers | +------------------------------+ ``` ================================================ FILE: mysql/run-statements-in-a-transaction.md ================================================ # Run Statements In A Transaction I'm connecting to a production MySQL database to make some changes to a specific user account. That means I am going to run an `update` statement and I expect that statement to affect exactly *one* row in the `users` table. If I run the `update` statement in a transaction, then I can verify all looks good before committing those changes. And importantly, I can rollback the changes if anything looks off. ```sql > start transaction; > update users set roles = 'admin' where id = '1234'; Query ok, 1 row affected > select * from users where id = '1234'; -- check that all looks good > commit; ``` In the above case, all looked good, so I ran `commit`. If more rows than I expected were affected or the changed record didn't look right, I could instead `rollback`. None of those changes would make it into live production data. [source](https://dev.mysql.com/doc/refman/8.0/en/commit.html) ================================================ FILE: mysql/select-rows-after-an-offset.md ================================================ # Select Rows After An Offset When doing pagination and other queries for special-case scenarios, we may need to grab rows after a certain offset. There are two variations of the MySQL syntax for selecting rows after a certain offset. ```sql select * from events limit 100, 10; ``` This first query will grab up to 10 rows after applying an offset of 100. Typically we'll see a `limit` clause with just one value which represents how many rows to limit the result set to. However, if we optionally include `N, ` in the middle of that clause. Whatever number `N` is will be the offset. Another way to write this is: ```sql select * from events limit 10 offset 100; ``` This gets the same result: 10 rows after an offset of 100. This is perhaps a bit more straightforward and reduces the chance that we forget which value is which like we might in the first syntax variation. Note: row ordering is only deterministic if you specify an order. To get consistent results with `offset`, you'll most likely want to be specifying an `order by` clause as well. [source](https://dev.mysql.com/doc/refman/8.0/en/select.html) ================================================ FILE: mysql/set-value-on-null-json-column.md ================================================ # Set Value On Null JSON Column To set a key-value pair on a JSON field, you can reach for [MySQL's `json_set`](https://dev.mysql.com/doc/refman/8.0/en/json-modification-functions.html#function_json-set) or one of [the other JSON setter functions](https://dev.mysql.com/doc/refman/8.0/en/json-modification-functions.html). However, if the JSON field you are updating is `null`, you might get an unexpected result. ```sql > update User set metadata = json_set(metadata, '$.discord_id', 'discord_123') where id = 123; Query OK, 0 rows affected (0.00 sec) Rows matched: 1 Changed: 0 Warnings: 0 ``` We can see that the `where` clause matched on a single row as expected, but right above that it says _0 rows affected_. What happened? The `json_set` function is not able to set a key-value pair on `null`. It needs a JSON object to work on. There are a number of ways to get around this. I find that [`coalesce`](https://dev.mysql.com/doc/refman/8.0/en/comparison-operators.html#function_coalesce) is a natural way to handle this. If `metadata` happens to be `null`, then we _coalesce_ it to `'{}'` (an empty object). ```sql > update User set metadata = json_set(coalesce(metadata, '{}'), '$.discord_id', 'discord_123') where id = 123; Query OK, 1 row affected (0.02 sec) Rows matched: 1 Changed: 1 Warnings: 0 ``` It updates as expected. That same statement will work on a row where `metadata` already contains a JSON object since the `coalesce` will resolve to that instead of the empty object. ================================================ FILE: mysql/show-create-statement-for-a-table.md ================================================ # Show Create Statement For A Table In MySQL, you can get a quick rundown of a table using `describe users`. An alternative to this approach is to have MySQL show the `create` statement for a table. ```sql > show create table users\G *************************** 1. row *************************** Table: users Create Table: CREATE TABLE `users` ( `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, `first_name` varchar(80) NOT NULL, `last_name` varchar(80) NOT NULL, `email` varchar(80) NOT NULL, `middle_initial` varchar(80) NOT NULL, PRIMARY KEY (`id`), UNIQUE KEY `unique_email` (`email`) ) ENGINE=InnoDB DEFAULT CHARSET=latin1 ``` This includes some additional information like primary key and index information. It is also a great way to study the SQL that it takes to create all the facets of a table. See the [`show create table` docs](https://dev.mysql.com/doc/refman/5.7/en/show-create-table.html) for more details. h/t Jake Worth ================================================ FILE: mysql/show-indexes-for-a-table.md ================================================ # Show Indexes For A Table When describing a table, such as the table `users`: ```sql > describe users; +------------+-----------------------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +------------+-----------------------+------+-----+---------+----------------+ | id | mediumint(8) unsigned | NO | PRI | NULL | auto_increment | | first_name | varchar(80) | NO | | NULL | | | last_name | varchar(80) | NO | | NULL | | | email | varchar(80) | NO | UNI | NULL | | +------------+-----------------------+------+-----+---------+----------------+ ``` I can see in the `Key` column that there is a primary key and a unique key for this table on `id` and `email`, respectively. These keys are indexes. To get more details about each of the indexes on this table, we can use the [`show indexes`](https://dev.mysql.com/doc/refman/5.7/en/show-index.html) command. ```sql > show indexes in users; +-------+------------+--------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+ | Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment | +-------+------------+--------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+ | users | 0 | PRIMARY | 1 | id | A | 0 | NULL | NULL | | BTREE | | | | users | 0 | unique_email | 1 | email | A | 0 | NULL | NULL | | BTREE | | | +-------+------------+--------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+ ``` ================================================ FILE: mysql/show-tables-that-match-a-pattern.md ================================================ # Show Tables That Match A Pattern An unfamiliar database with tons of tables can be a difficult thing to navigate. You may have an idea of the kind of table you are looking for based on a domain concept you've seen elsewhere. You can pare down the results returned by `show tables` by including a `like` clause with a pattern. For example, this statement will show me only tables that have the word `user` in them: ```sql > show tables like '%user%'; +-------------------------------+ | Tables_in_jbranchaud (%user%) | +-------------------------------+ | admin_users | | users | +-------------------------------+ ``` ================================================ FILE: neovim/allow-neovim-to-copy-paste-with-system-clipboard.md ================================================ # Allow Neovim To Copy/Paste With System Clipboard By default, Neovim uses some internal registers for managing the values that have been copied (`y`) and what should be pasted (`p`). These registers are independent from the system clipboard, so a value copied from the browser will not show up when you hit `p` in Neovim (or Vim). If you'd like to create a more seamless and cohesive copy/paste experience for yourself, you can instruct Neovim to read from and write to the system clipboard when copy/paste actions happen. This is accomplished with a `provider` that instructs Neovim to use the system clipboard directly for all copy/paste operations. ```vimscript " ~/.vimrc set clipboard+=unnamedplus ``` Setting the `clipboard` option to include `unnamedplus` enables that provider "which transparently uses shell commands to communicate with the system clipboard or any other clipboard 'backend'." So, for Mac, `pbcopy` and `pbpaste`. See `:h provider-clipboard` for more details. ================================================ FILE: neovim/create-user-command-to-open-init-config.md ================================================ # Create User Command To Open Init Config I'm experimenting with a fresh Neovim configuration using [kickstart](https://github.com/nvim-lua/kickstart.nvim). That means I'm frequently navigating to my `init.lua` file to add and adjust things that I find are missing from my workflow. I got tired of typing out the path—in my case `~/.config/nvim/init.lua`—every single time I wanted to edit it. So, I typed out that path one last time so that I could add a custom user command. ``` -- Open this config file vim.api.nvim_create_user_command( 'Config', "e ~/.config/nvim/init.lua", {bang = true, desc = "Open init.lua Neovim config"} ) ``` This uses [the lua command API](https://neovim.io/doc/user/api.html#api-command) to create a user-defined command. When I invoke `:Config` from the Neovim command prompt and hit enter, Neovim will effectively replace that command with the second argument to that command — `:e ~/.config/nvim/init.lua`. Which opens me up to the config file. ================================================ FILE: neovim/jump-between-changes-in-current-file.md ================================================ # Jump Between Changes In Current File With the [gitsigns.nvim plugin](https://github.com/lewis6991/gitsigns.nvim) for Neovim, I get some handy Git-related capabilities like gutter highlighting of additions, deletions, and changes to lines in the current file. These contiguous sections of modification to the versioned state of a file are called hunks. Here are two mappings (in Lua) for gitsigns that allow me to jump to the next (`]h`) or previous (`[h`) hunk in the current file. ```lua ---@type LazyKeysSpec[] M.gitsigns_mappings = { -- Navigation { ']h', function() if vim.wo.diff then vim.cmd.normal { ']c', bang = true } else require('gitsigns').nav_hunk 'next' end end, desc = 'Next Hunk', }, { '[h', function() if vim.wo.diff then vim.cmd.normal { '[c', bang = true } else require('gitsigns').nav_hunk 'prev' end end, desc = 'Prev Hunk', }, } ``` This is particularly useful when I've just opened a big file and I want to jump directly to active changes in that file. I got this mapping directly from [Dorian's dotfiles](https://github.com/dkarter/dotfiles). ================================================ FILE: neovim/run-a-lua-statement-from-the-command-prompt.md ================================================ # Run A Lua Statement From The Command Prompt The `:lua` command is provided by Neovim as a way to execute a Lua _chunk_. I can use it to, for instance, execute a print statement. ``` :lua print('Hello, World!') ``` I could print out something more interesting like the full path of the current file using `vim.fn.expand` with `%`. ``` :lua print(vim.fn.expand('%')) ``` Or as the helpfiles point out, I can see the value of some expression by including a preceeding `=`. ``` :lua =jit.version ``` See `:h :lua` for more details. ================================================ FILE: neovim/run-nvim-with-factory-defaults.md ================================================ # Run nvim With Factory Defaults Most of the fun of using Neovim is tailoring it to your exact needs with custom configurations. Your configuration can be made up of environment variables, `init.lua`/`init.vim`, and user directories on the `runtimepath`. Perhaps though, you want to load neovim with its "factory defaults". You want to ignore all your custom config and your _shada_ (shared data) file. I wanted to do just that recently to verify that neovim has the `ft-manpage` plugin enabled by default (as opposed to enabled somewhere in the labryinth of my config files). The `--clean` flag does just this. It loads built-in plugins, but none of the user defined config. ```bash $ nvim --clean ``` This is different than `nvim -u NONE` which excludes all plugins, including built-in ones. See `man nvim` and `:help --clean` for more details. ================================================ FILE: neovim/set-up-vim-plug-with-neovim.md ================================================ # Set Up Vim-Plug With Neovim To get [vim-plug](https://github.com/junegunn/vim-plug) working with Neovim, it needs to be installed in a known autoload directory. The help files say that "plugins installed by user" should be located in the data home directory at `stdpath("data")` under `site`. For me (on OSX) the data home directory is `~/.local/share/nvim`, so `~/.local/share/nvim/site`. Under this site directory, along with any directories on the `runtimepath`, Neovim looks for various runtime files and subdirectories. This includes the `/autoload` directory. That's where you want to install `vim-plug`. ```bash sh -c 'curl -fLo "${XDG_DATA_HOME:-$HOME/.local/share}"/nvim/site/autoload/plug.vim --create-dirs \ https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim' ``` This shell statement uses [the `:-` parameter expansion](../unix/provide-a-fallback-value-for-unset-parameter.md) to specify a path using either the `XDG_DATA_HOME` or `$HOME` as a fallback. This ends up resolving to `~/.local/share`, so the `plug.vim` file is placed in `~/.local/share/nvim/site/autload/`. See `:h runtimepath` for more details. ================================================ FILE: netlify/override-the-default-yarn-version.md ================================================ # Override The Default Yarn Version When you first deploy an app to [Netlify](https://www.netlify.com/), the current default [Yarn](https://yarnpkg.com/) version will be locked in. This Yarn version will be used for future builds and deployments. You can override the default Yarn version by including the `YARN_VERSION` environment variable. Set it to the desired major and minor version in the app settings panel. The next deployment will use that new version. [source](https://community.netlify.com/t/default-yarn-version-is-now-1-17/2297) ================================================ FILE: next-auth/adjust-the-shape-of-the-user-type.md ================================================ # Adjust The Shape Of The User Type Let's say we want extra attributes on our `user` object that gets passed around as part of authentication with [NextAuth.js](https://next-auth.js.org/). If we're using a database adapter, it's because we have added columns to the `User` table. Or it may be that we are adding extra attributes to the body of the JWT token. Either way, we need the underlying `User` type (rather, interface) to reflect that. Within a `declare module` block for NextAuth, we can define a `User` interface with any additional properties that we want. This adds `roles` as a required `string` type. ```typescript declare module "next-auth" { interface User { // ...other properties roles: string; } } ``` Some natural places to add this declaration are in the auth file itself (e.g. `src/server/auth.ts`). Or we can put it in a dedicated top-level `next-auth.d.ts` file as long as we tell our `tsconfig.json` to include it: ```json { ... "include": [ "src/**/*", "process.d.ts", "next-auth.d.ts" ], ... } ``` ================================================ FILE: nextjs/avoid-conflicting-files.md ================================================ # Avoid Conflicting Files When Next.js is bundling and building your project, it will get completely tripped up by any instance of conflicting project files. What I mean by conflicting project files are two JavaScript or TypeScript (or flavors of JSX files) that would resolve to the same thing. Here is one example where the extensions differ: ``` src/pages/welcome.tsx src/pages/welcome.jsx ``` Here is another example where the paths differ but the bundled result would conflict: ``` src/pages/welcome.tsx src/pages/welcome/index.tsx ``` If you have any instances of these conflicting files, you'll be presented with a beguiling and cryptic error message when trying to run the dev server. ``` TypeError [ERR_INVALID_ARG_TYPE]: The "to" argument must be of type string. Received undefined at new NodeError (node:internal/errors:405:5) at validateString (node:internal/validators:162:11) at Object.relative (node:path:1191:5) at Watchpack. (/my_app/node_modules/.pnpm/next@14.2.5_@babel+core@7.24.9_react-dom@18.3.1_react@18.3.1/node_modules/next/dist/server/lib/router-utils/setup-dev-bundler.js:381:55) { code: 'ERR_INVALID_ARG_TYPE' } ``` One of those files needs to go. Remove one of them and you'll be good to go. ================================================ FILE: nextjs/create-files-and-directories-for-dynamic-routes.md ================================================ # Create Files And Directories For Dynamic Routes [Next.js](https://nextjs.org/) allows you to go beyond static, predefined pages and routes with [dynamic routing](https://nextjs.org/docs/routing/dynamic-routes). The common example is a `posts` route that includes a _slug_ to dynmically reference a particular post. The template for that page can be defined at `pages/posts/[slug].js`. Notice the square brackets around the slug, that tells Next that it is a dynamic route and whatever matches against the slug should be included in `router.query` as `slug`. Let's try to create that file: ```bash $ touch pages/posts/[slug].js zsh: no matches found: pages/posts/[slug].js ``` That failed. To create this kind of file from the command-line, you are going to need to escape the square brackets: ```bash $ touch pages/posts/\[slug\].js ``` You can do the same if you use dynamic routing in your directory structure: ```bash $ mkdir -p pages/posts/\[year\]/\[month\]/\[day\] ``` And now we have the following structure: ```bash $ exa --tree pages/posts pages/posts ├── [slug].js └── [year] └── [month] └── [day] ``` ================================================ FILE: nextjs/define-url-redirects-in-the-next-config.md ================================================ # Define URL Redirects In The Next Config In [Add Web Server Layer Redirects](vercel/add-web-server-layer-redirects.md), I explained how to define URL redirects to your [Vercel](https://vercel.com/) configuration for a [Next.js](https://nextjs.org/) app. Because these redirect rules are defined in `vercel.json` which is processed at the time of deployment on the Vercel platform, you are unable to experience these redirects with your local dev instance of the app. That could be misleading and cause confusion during development. Instead, you can define your redirects in `next.config.js` as part of the Next.js app's configuration. When locally running the Next dev server, these redirects will be processed and active. Here is an example of these redirects in `next.config.js`: ```javascript const nextConfig = { async redirects() { return [ { source: "blog/old-blog-post-name", destination: "blog/new-blog-post-name", permanent: true, }, { source: "/store", destination: "store.example.com" permanent: true, }, ] }, } ``` These will be 308 Permanent Redirects because of `permanent: true`. You can change that to `false` to make them into 307s. ================================================ FILE: nextjs/fetch-does-not-work-in-api-serverless-function.md ================================================ # Fetch Does Not Work In API Serverless Function Next.js ships with [its own implementation of `fetch`](https://nextjs.org/docs/app/api-reference/functions/fetch) that extends the [native `fetch` API](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API). I ran into a bug recently that was only reproducible in production. I had a Next.js API function that was `POST`ing to another API endpoint using `fetch`. I tested it several ways in development. It worked great. However, once the feature was in production, it was silently failing. After some `console.log` debugging, I discovered that the target API was getting an empty `body` in the `POST` request. I don't know the specifics of why, but somehow the `fetch` implementation running in the Vercel serverless function environment apparently strips out the `body` of a POST request. The solution, for me, was to add the [`node-fetch`](https://github.com/node-fetch/node-fetch) package and import that version of fetch in my API function. Once I made that change, my feature was working again. [source](https://github.com/vercel/vercel/discussions/4971) ================================================ FILE: nextjs/make-environment-variable-publicly-available.md ================================================ # Make Environment Variable Publicly Available You can define environment variables in `.env.development` and `.env.production` files for use in your app. This is a great way to seamlessly swap out values that are specific to each environment. For instance, you might have a base URL for API requests that points to a local server in development and then to the live server in production. You could define that value like so: ``` API_BASE_URL=localhost:3000/api/v1 ``` The problem you'll quickly run into is in trying to access that value from any client-side pages or components. ``` process.env.API_BASE_URL //=> undefined ``` Next.js is trying to help us out here. Environment variables are often times private keys and other secrets that shouldn't be bundled into our public client code. So Next.js excludes all env vars from the build by default. Our API's base URL is not a secret though. The way to make env vars like that publicly avilable is to prepend them with `NEXT_PUBLIC_`. ``` NEXT_PUBLIC_API_BASE_URL=localhost:3000/api/v1 ``` Now, it is available anywhere in your client and server code: ``` process.env.NEXT_PUBLIC_API_BASE_URL //=> 'localhost:3000/api/v1' ``` [source](https://nextjs.org/docs/basic-features/environment-variables#exposing-environment-variables-to-the-browser) ================================================ FILE: nextjs/match-middleware-on-groups-of-paths.md ================================================ # Match Middleware On Groups Of Paths The Next.js middleware takes an array of path matchers in its config to decide what pages to apply middleware to. These paths can use regex via [`path-to-regexp`](https://github.com/pillarjs/path-to-regexp#path-to-regexp-1). Let's say I'm using middleware to do authentication checks for certain pages. If I want to visits to my `/dashboard` page to run through middleware, I can configure my matcher like so: ```javascript export const config = { matcher: ["/dashboard"] }; ``` If we want to match against `/dashboard` and any possible sub-structure to that path, we can apply some regex: ```javascript export const config = { matcher: ["/dashboard/:all*"] }; ``` That will match `/dashboard`, `/dashboard/hello`, `dashboard/hello/world`, etc. Lastly, let's say I only want to match routes under the `/dashboard` route. I can replace the `*` (zero-or-more matches) with a `+` (one-or-more matches): ```javascript export const config = { matcher: ["/dashboard/:all+"] }; ``` That will match `/dashboard/hello`, `/dashboard/hello/world`, etc., but not `/dashboard`. [source](https://nextjs.org/docs/app/building-your-application/routing/middleware) ================================================ FILE: nextjs/organize-pages-in-route-groups.md ================================================ # Organize Pages In Route Groups With the Next.js App Router we can organize pages without affecting the URL path structure by nesting those directories and pages within a _Route Group_. A Route Group is directory where the name is surrounded by parentheses, e.g. `/(symbols)`. For instance, in my [Ruby Operator Lookup](https://www.visualmode.dev/ruby-operators) project, I have the following structure: ```bash $ exa --true src/app/ruby-operators src/app/ruby-operators ├── (symbols) │ ├── ampersand │ │ └── page.mdx │ ├── arbitrary-keyword-arguments │ │ └── page.mdx │ ├── asterisk │ │ └── page.mdx │ ├── at-symbol │ │ └── page.mdx │ ├── backtick │ │ └── page.mdx │ ├── ... │ └── underscore │ └── page.mdx ├── client-layout.tsx ├── layout.tsx ├── page.tsx └── wrapper.ts ``` I'm able to organize all the different symbols and operators under a separate directory `/(symbol)/`. That makes development easier. However, the end result routing still has each symbol located directly under `/ruby-operators/`, e.g. `/ruby-operators/ampersand`. [source](https://nextjs.org/docs/app/getting-started/project-structure#route-groups) ================================================ FILE: nextjs/precedence-of-dot-env-files.md ================================================ # Precedence Of Dot Env Files _Dot Env_ files like `.env`, `.env.development`, `.env.local`, etc. are one of the main ways to configure your Next.js app across various environments. It's not uncommon to see several different `.env*` files in production app that is under active development. Here is an example of almost every variant in play: ```bash $ ls -a -1 .env* .env .env.development .env.development.local .env.development.local.example .env.local .env.production .env.test ``` So, how does Next.js decide which files to load and in what order? It will always attempt to load `.env` and `.env.local` (except in `test`) if those exist. It will also look for environment-specific files based on the `NODE_ENV` (which can be one of `development`, `test`, or `production`). So, in `development`, the `.env.development` and `.env.development.local` will be loaded. Something like `.env.development.local.example` isn't on the list, but rather is a convention for a dotenv file's template. As for the order, the environment itself (your system's environment variables) which are present in `process.env` take the highest precedence. After that, it looks in any of the follow present files in this order, stopping once it finds what it is looking for: - `.env.$(NODE_ENV).local` - `.env.local` - `.env.$(NODE_ENV)` - `.env` [source](https://nextjs.org/docs/pages/building-your-application/configuring/environment-variables#environment-variable-load-order) ================================================ FILE: nextjs/push-a-route-with-a-url-object.md ================================================ # Push A Route With A URL Object There are two ways of using the Next.js router to transition to another route using [`push`](https://nextjs.org/docs/api-reference/next/router#with-url-object). The first, and perhaps more common, is by passing it a string. ```javascript router.push('/search?tag=react') ``` This is great for simple routes. When routes require query params, this can lead to error-prone string interpolation. That's where the second way comes in. The second is to use a [URL Object](https://nextjs.org/docs/api-reference/next/router#with-url-object) instead of a string. ```javascript router.push({ pathname: '/search', query: { tag: 'react' } }) ``` Here we are working with an object. I find objects a bit easier to work with, than strings, when doing programmatic things. Especially when it comes to adding and removing query params. ================================================ FILE: nextjs/redirect-an-unauthorized-user.md ================================================ # Redirect An Unauthorized User With the Page Router in earlier next version, we could do a server-side authorization check in `getServerSideProps` and then return a [`redirect`](https://nextjs.org/docs/pages/api-reference/functions/get-server-side-props#redirect) response in order to redirect the user to a page they are authorized for. That might look something like this: ```javascript export async function getServerSideProps(context) { const session = await getServerAuthSession() const ability = getAbility({user: session?.user}) if (!ability.can('create', 'Post')) { return { redirect: { destination: '/posts', permanent: false, }, } } return { props: {}, } } ``` We can achieve the same thing with the App Router, but with a bit less code. The `next/navigation` package has a [`redirect` function](https://nextjs.org/docs/app/api-reference/functions/redirect) that we can invoke directly in a component. This will redirect the user instead of rendering the component to HTML. ```javascript import { redirect } from 'next/navigation' export default async function CreatePost() { const session = await getServerAuthSession() const ability = getAbility({user: session?.user}) if (!ability.can('create', 'Post')) { redirect('/posts') } // JSX follows return (...) } ``` ================================================ FILE: nextjs/remove-a-query-param-from-the-url.md ================================================ # Remove A Query Param From The URL Let's say you have a Next.js app. Sometimes users visit the app with special query params. You want to extract the value of those query params, do something with them, and then remove them from the URL. This can be done with [`next/router`](https://nextjs.org/docs/api-reference/next/router). Let's say this component loads while the app URL is `/home?code=123`. ```javascript import React, { useEffect } from "react"; import {useRouter} from "next/router"; function SomeComponent() { const router = useRouter(); useEffect(() => { // extract the value from the query params const { code, ...updatedQuery } = router.query; if (!!code) { // do something with the extract query param doSomethingWithCode(code); // create an updated router path object const newPathObject = { pathname: router.pathname, query: updatedQuery } // update the URL, without re-triggering data fetching router.push(newPathObject, undefined, { shallow: true }); } }, []) return ( ... ); } ``` After the mount, the URL will read `/home` and the code will have done something with the code value. This is accomplished by destructuring the target query param apart from the rest, constructing a new router path object with the rest of the query params, and then [pushing that route update _shallowly_](https://nextjs.org/docs/routing/shallow-routing) so that data doesn't get refetched. ================================================ FILE: nextjs/ship-public-assets-with-a-nextjs-app.md ================================================ # Ship Public Assets With A Next.js App A Next.js project includes a top-level `public` directory. Anything in this directory at build time will be publicly available. This is handy for things like a logo, cover image, or favicon. If I create an `images` directory in `public` and then place an SVG in it: ```bash $ ls public/images logo.svg ``` Then I can reference that image in the HTML or JSX of my app pages, such as in a `header.jsx` component. ```jsx const Header = () => {
    {/* a bunch of header and nav content */}
    } ``` Notice it is publicly available at `/images/logo.svg`. You can do this with other files as well. For instance, some kind of company brochure PDF could be placed in `public` and you could link to it as a download. [source](https://nextjs.org/docs/basic-features/static-file-serving) ================================================ FILE: phoenix/bypass-template-rendering.md ================================================ # Bypass Template Rendering Generally when rendering a response to a request in a Phoenix app, the controller will make a render call that targets a specific template. If it suits our needs, we can skip writing a template and bypass the template portion of the response pipeline by implementing our own `render` function directly in the view module. Consider the following route and controller action: ```elixir # web/router.ex get "/hello", UserController, :hello # web/controllers/user_controller.ex def hello(conn, _params) do render(conn, "hello.html") end ``` The render call would normally trigger a corresponding template function, but we bypass it by adding the following function to our view module: ```elixir # web/views/user_view.ex def render("hello.html", _assigns) do "Hello, World!" end ``` Visiting `/hello` will render a page with the view's layout and the words "Hello, World!". ================================================ FILE: phoenix/check-the-installed-version.md ================================================ # Check The Installed Version Check what the installed version of Phoenix is with the `-v` flag. ```bash $ mix phoenix.new -v Phoenix v1.2.0 ``` [source](http://shorts.jeffkreeftmeijer.com/2015/find-the-currently-installed-phoenix-version-number/) ================================================ FILE: phoenix/generate-new-app-without-brunch.md ================================================ # Generate New App Without Brunch By default when you create a new Phoenix app using `phx.new`, a set of files and configurations will be generated for [Brunch](http://brunch.io/). Though the Phoenix team decided to use Brunch, you don't have to. You may not want Phoenix to handle asset building or you may just prefer another tool. Either way, if you'd like to opt out, you can include the `--no-brunch` flag when generating the project. ```bash $ mix phx.new --no-brunch my_app ``` If you have an existing project that you'd like to remove Brunch from, there is some information in [Phoenix's Static Assets documentation](http://phoenixframework.org/blog/static-assets). ================================================ FILE: phoenix/render-a-template-to-a-string.md ================================================ # Render A Template To A String Templates in a [Phoenix](http://www.phoenixframework.org/) application ultimately get compiled to functions that can be quickly rendered with the necessary data. We can take a look at how a template will be rendered using [`Phoenix.View.render_to_string/3`](https://hexdocs.pm/phoenix/Phoenix.View.html#render_to_string/3). First, we need a template: ```elixir # user.html.eex

    <%= @user.first_name %>

    <%= @user.username %> (<%= @user.email %>)
    ``` We can then render that template for the view with some user: ```elixir > user = %User{first_name: "Liz", last_name: "Lemon", username: "llemon", email: "lizlemon@nbc.com"} %MyApp.User{...} > Phoenix.View.Render_to_string(MyApp.UserView, "user.html", user: user) "

    Liz

    \n
    llemon (lizlemon@nbc.com)
    \n" ``` ================================================ FILE: phoenix/serve-static-assets-from-custom-directory.md ================================================ # Serve Static Assets From Custom Directory When you new up a Phoenix project, an `endpoint.ex` file will be generated. This file is full of different plugs for handling incoming traffic. The `Plug.Static` declaration specifies how your application will handle and serve requests for static files. ```elixir plug Plug.Static, at: "/", from: :my_app, gzip: false, only: ~w(css fonts images js favicon.ico robots.txt) ``` The `from` options declares where these static files are located. In this case it references our application (`:my_app`) as the target which will translate to its `priv/static` directory. If you instead want to serve your files from a different, custom directory, you can replace it with the path to that directory. ```elixir plug Plug.Static, at: "/", from: "priv/my_frontend/static", gzip: false, only: ~w(css fonts images js favicon.ico robots.txt) ``` [source](https://hexdocs.pm/plug/Plug.Static.html) ================================================ FILE: phoenix/specifying-the-digest-directory.md ================================================ # Specifying The Digest Directory By default, Phoenix targets `priv/static` when preparing digested assets for production. This process happens when running `mix phx.digest`. If you are doing some custom work with your assets such that they are in a different location, you'll need to tell Phoenix where to look. To do this, just include an optional path argument. ```bash $ mix phx.digest path/to/my/assets ``` The digests will be put in that target directory. If you'd like to specify a different output directory, such as `priv/static`, include the `-o` flag. ```bash $ mix phx.digest path/to/my/assets -o priv/static ``` ================================================ FILE: phoenix/specifying-the-server-port.md ================================================ # Specifying The Server Port Running `mix phx.server` for a Phoenix project with the default settings will attach the server to port `4000`. If you'd like to use a different port in development, you can change it in `config/dev.exs`. ```elixir config :my_app, MyApp.Web.Endpoint, http: [port: 4444], ... ``` Alternatively, you can allow it to be configurable from the command line with an environment variable and a fallback port. ```elixir config :my_app, MyApp.Web.Endpoint, http: [port: System.get_env("PORT") || 4000], ... ``` Running ```bash $ PORT=4444 mix phx.server ``` will launch the server on port `4444`. ================================================ FILE: planetscale/see-what-databases-you-have-access-to.md ================================================ # See What Databases You Have Access To Assuming you have the `pscale` CLI installed and you've authenticated with it, you can run the following to view available databases. ```bash $ pscale database list NAME KIND CREATED AT UPDATED AT ----------- ------- ------------- ------------- bookshelf mysql 3 years ago 3 years ago ``` I'm not very active on my personal account. Planetscale is a multi-tenant SaaS though. I can switch from my personal `org` to another team I have access to. ```bash $ pscale org switch another-team ``` And then from there I can run `pscale database list` again to see what databases I have access to from this other organization. See `pscale database help` and `pscale org help` for more details. ================================================ FILE: planetscale/seed-production-data-into-another-branch.md ================================================ # Seed Production Data Into Another Branch When you [create a Planetscale branch](https://planetscale.com/docs/reference/branch) off `main`, it will only copy over the schema. No data will be copied over to that new branch. You can copy data over from the initial branch (`main`) in two steps from the CLI. First, create a dump of the branch. Then restore the dump into your new branch. ```bash $ pscale database dump database-name main --output ./dump ``` That creates SQL files locally in the `dump` folder with both schema and data statements. That folder of SQL files can then be restored into one of your branches. ```bash $ pscale database restore-dump database-name branch-name --dir ./dump --overwrite-tables ``` The `--overwrite-tables` flag is needed because your branch's existing schema will conflict with the `create` schema statements in the SQL files. You can `pscale shell` into that branch and run a `select ...` statement to check out the data. ================================================ FILE: pnpm/execute-a-command-from-the-workspace-root.md ================================================ # Execute A Command From The Workspace Root [`pnpm`](https://pnpm.io/) is designed to work with monorepos. In a monorepo project you'll have a root `package.json` (in the top-level directory of the repository) as well as individual `package.json` files per app and package. If you're working from within the directory of one of your apps and you invoke a command, `pnpm` should execute that command for this app. The `pnpm` CLI does give you control to execute the command from the workspace root instead if you'd like. A likely setup is that both your root `package.json` and your individual app `package.json` files have a `build` command. Invoking the build command from `apps/my-app`: ```bash pnpm build ``` will run the `my-app` build command, as specified in its `package.json`. You could instead throw in [the `--workspace-root` flag](https://pnpm.io/pnpm-cli#-w---workspace-root). ```bash pnpm --workspace-root build ``` This will ignore whatever subdirectory you are in and invoke the `build` command defined in your top-level `package.json` file. ================================================ FILE: pnpm/install-command-runs-for-entire-workspace.md ================================================ # Install Command Runs For Entire Workspace When you run [`pnpm install`](https://pnpm.io/cli/install) in a monorepo, it will run from the context of the workspace root. That means it will install dependencies for your entire monorepo across apps and packages. Even if you are in a subdirectory for a specific project with its own `package.json`, running `pnpm install` will install dependencies for the entire workspace. If you want to install dependencies only for a specific project or a subset of projects, you can use [the `--filter` flag](https://pnpm.io/cli/install#--filter-package_selector). ================================================ FILE: pnpm/list-the-installed-version-of-a-specific-package.md ================================================ # List The Installed Version Of A Specific Package `pnpm` is a Node-ecosystem package manager with first-class support for monorepos. In a monorepo with many apps and packages that each have their own `package.json` file, it can be tricky to know what version of a package is installed for a specific app or package. The `pnpm list` command can help with that. First navigate to a specific app or package whose dependencies you want to know about. Then run a command like the following, replacing `@trpc/next` with your package of interest. ```bash $ pnpm list '@trpc/next' Legend: production dependency, optional only, dev only epic-react /Users/jbranchaud/code/clients/egghead/products/apps/epic-react dependencies: @trpc/next 10.7.0 ``` It tells you the exact version of that dependency that is isntalled for the current app/package. This command can also be used with regex. Let's say you want to know about all `next`-related dependencies. You could do the following: ```bash $ pnpm list '*next*' ``` [source](https://pnpm.io/cli/list) ================================================ FILE: postgres/a-better-null-display-character.md ================================================ # A Better Null Display Character By default, `psql` will display null values with whitespace. This makes it difficult to quickly identify null values when they appear amongst a bunch of other data. You can pick a better display value for null characters with `\pset null`. My preference is the following: ``` \pset null 'Ø' ``` I have this in my `.psqlrc` file so that it is used by default every time. ================================================ FILE: postgres/add-foreign-key-constraint-without-a-full-lock.md ================================================ # Add Foreign Key Constraint Without A Full Lock Adding a foreign key constraint to a large production table can cause a full table lock resulting in downtime. This is because the entire table needs to be scanned to check that the constraint is valid. The amount of locking, and ultimately the impact on your app, can be reduced by spreading this action across two commands. First is to add the constraint without checking that all the existing records are valid. ```sql alter table books add constraint fk_books_authors foreign key (author_id) references authors(id) not valid; ``` The constraint will be added immediately and any subsequent inserts or updates will be subject to the new foreign key constraint. The second step is to make this constraint valid for all the existing rows. ```sql alter table books validate constraint fk_books_authors; ``` This "validation acquires only a SHARE UPDATE EXCLUSIVE lock on the table being altered." This is lower impact than a full table lock. [Source](https://www.postgresql.org/docs/current/sql-altertable.html#SQL-ALTERTABLE-NOTES) ================================================ FILE: postgres/add-on-delete-cascade-to-foreign-key-constraint.md ================================================ # Add ON DELETE CASCADE To Foreign Key Constraint The `alter table` command lets you do quite a bit. But when it comes to altering existing constraints, there is not much you can do. If you want to add an `on delete cascade` to an existing foreign key constraint, you are going to need two statements. The first statement will drop the constraint and the second statement will recreate it with the addition of the `on delete` clause. Furthermore, you'll want to do this in a transaction to ensure the integrity of your data during the transition between indexes. Here is an example: ```sql begin; alter table orders drop constraint orders_customer_id_fkey; alter table orders add constraint orders_customer_id_fkey foreign key (customer_id) references customers (id) on delete cascade; commit; ``` [source](http://stackoverflow.com/questions/10356484/how-to-add-on-delete-cascade-constraints) ================================================ FILE: postgres/add-unique-constraint-using-existing-index.md ================================================ # Add Unique Constraint Using Existing Index Adding a unique constraint to an existing column on a production table can block updates. If we need to avoid this kind of locking for the duration of index creation, then we can first create the index concurrently and then use that existing index to back the unique constraint. ```sql create index concurrently users_email_idx on users (email); -- wait for that to complete alter table users add constraint unique_users_email unique using index users_email_idx; ``` First, we concurrently create the index. The time this takes will depend on how large the table is. That's the blocking time we are avoiding with this approach. Then once that completes we can apply a unique constraint using that preexisting index. Note: if a non-unique value exists in the table for that column, adding the constraint will fail. You'll need to deal with that _duplicate_ value first. [source](https://dba.stackexchange.com/questions/81627/postgresql-9-3-add-unique-constraint-using-an-existing-unique-index) ================================================ FILE: postgres/adding-composite-uniqueness-constraints.md ================================================ # Adding Composite Uniqueness Constraints There are two ways in Postgres to create a composite uniqueness constraint; that is, a constraint that ensures that the combination of two or more values on a table only appear once. For the following two code snippets, assume that we have a table relating Pokemon and Trainers and that our domain restricts each Trainer to only having at most one of each Pokemon. The first approach is to create a `constraint` directly on the table: ```sql alter table pokemons_trainers add constraint pokemons_trainers_pokemon_id_trainer_id_key unique (pokemon_id, trainer_id); ``` The second approach is to create a unique index: ```sql create unique index pokemons_trainers_pokemon_id_trainer_id_idx on pokemons_trainers (pokemon_id, trainer_id); ``` ================================================ FILE: postgres/aggregate-a-column-into-an-array.md ================================================ # Aggregate A Column Into An Array PostgreSQL's `array_agg` function can be used to aggregate a column into an array. Consider the following column: ```sql > select num from generate_series(1,5) as num; num ----- 1 2 3 4 5 ``` By wrapping the `array_agg` aggregate function around `num` we are able to *aggregate* the values in that column into an array, like so: ```sql > select array_agg(num) from generate_series(1,5) as num; array_agg ------------- {1,2,3,4,5} ``` See the docs on [aggregate functions](http://www.postgresql.org/docs/current/static/functions-aggregate.html) for more details. ================================================ FILE: postgres/assumed-radius-of-the-earth.md ================================================ # Assumed Radius Of The Earth Using the [`earthdistance`](https://www.postgresql.org/docs/8.3/static/earthdistance.html) module, we can get the assumed radius of the earth (in meters). ```sql > create extension cube; CREATE EXTENSION > create extension earthdistance; CREATE EXTENSION > select earth(); earth --------- 6378168 ``` ================================================ FILE: postgres/auto-expanded-display.md ================================================ # Auto Expanded Display By default, postgres has expanded display turned off. This means that results of a query are displayed *horizontally*. At times, the results of a query can be so wide that line wrapping occurs. This can make the results and their corresponding column names rather difficult to read. In these situations, it is preferable to turn on expanded display so that results are displayed *vertically*. The `\x` command can be used to toggle expanded display on and off. Having to toggle expanded display on and off depending on the way a particular set of results is going to display can be a bit tedious. Fortunately, running `\x auto` will turn on auto expanded display. This means postgres will display the results normally when they fit and only switch to expanded display when it is necessary. h/t Jack Christensen ================================================ FILE: postgres/between-symmetric.md ================================================ # Between Symmetric PostgreSQL's `between` construct allows you to make a comparison _between_ two values (numbers, timestamps, etc.). ```sql > select * from generate_series(1,10) as numbers(a) where numbers.a between 3 and 6; a --- 3 4 5 6 ``` If you supply an empty range by using the larger of the two values first, an empty set will result. ```sql > select * from generate_series(1,10) as numbers(a) where numbers.a between 6 and 3; a --- ``` Tacking `symmetric` onto the `between` construct is one way to avoid this issue. ```sql > select * from generate_series(1,10) as numbers(a) where numbers.a between symmetric 6 and 3; a --- 3 4 5 6 ``` > BETWEEN SYMMETRIC is the same as BETWEEN except there is no requirement > that the argument to the left of AND be less than or equal to the argument > on the right. If it is not, those two arguments are automatically swapped, > so that a nonempty range is always implied. [source](https://www.postgresql.org/docs/current/functions-comparison.html#:~:text=BETWEEN%20SYMMETRIC%20is%20like%20BETWEEN,nonempty%20range%20is%20always%20implied.) ================================================ FILE: postgres/capitalize-all-the-words.md ================================================ # Capitalize All The Words PostgreSQL provides the string function `initcap()` as a way of capitalizing all words. In the process, it cleans up the casing of the remaining parts of the words. Here are some examples of how it works. ```sql > select initcap('hello, world'); initcap -------------- Hello, World > select initcap('HELLO, WORLD'); initcap -------------- Hello, World ``` See the [String Functions and Operators docs](https://www.postgresql.org/docs/current/static/functions-string.html) for more details. ================================================ FILE: postgres/change-the-current-directory-for-psql.md ================================================ # Change The Current Directory For psql When you start a `psql` session, your current directory is what `psql` will use as its current directory. This is important for meta-commands that use relative paths based on the current directory -- for instance, the `\i` meta-command for importing files. You can change the current directory within a `psql` session using the `\cd` meta-command. If my current directory is `home` and there is a `sql` directory in `home, these commands will do the following: ```sql \! pwd -- /home \cd sql \! pwd -- /home/sql ``` The `\cd` meta-command even supports tab completion relative to the current directory. You can also change to your home directory using just `\cd`. ================================================ FILE: postgres/change-the-owner-of-a-sequence.md ================================================ # Change The Owner Of A Sequence Sequence ownership is one of those things in PostgreSQL that is just under the surface and so it is easy to not know about it. If, however, you are doing a live migration where you are swapping out a column or entire table, you'll need to know about it. For instance, consider migrating a primary key column from `int` to `bigint`. Let's say you construct and backfill the new `bigint` column and then swap it out with the `int` column. Run a sequence ownership query like [the ones discussed in this article](https://sadique.io/blog/2019/05/07/viewing-sequence-ownership-information-in-postgres/) and you'll see that the original `int` column still owns the sequence. If you try to drop `old_id`, you'll fortunately get a warning from Postgres: ```sql alter table cats drop column old_id; ERROR: cannot drop column old_id of table cats because other objects depend on it DETAIL: default value for column id of table cats depends on sequence cats_id_seq HINT: Use DROP ... CASCADE to drop the dependent objects too. ``` The `DROP ... CASCADE` suggestion is not the thing to do here. Instead, you'll want to update the ownership of the sequence to the _new_ `id` column: ```sql alter sequence cats_id_seq owned by cats.id; ``` [source](https://www.postgresql.org/docs/current/sql-altersequence.html) ================================================ FILE: postgres/check-if-clusters-are-upgrade-compatible.md ================================================ # Check If Clusters Are Upgrade Compatible One of the ways to upgrade a PostgreSQL database from one server version to another is to use the built-in `pg_upgrade` command. This can be faster and require fewer manual steps than something like a `pg_dump` and `pg_restore`. However, before you run the `pg_upgrade` command for real, you should check that the target database is compatible with the current database. To do this, write your `pg_update` command with all the flags you need and then tack on `--check` at the end. This does a dry-run reporting the results of a series of consistency checks. Here is what a successful _check_ looks like: ```bash $ /usr/local/opt/postgresql@13/bin/pg_upgrade \ --old-bindir $HOME/.asdf/installs/postgres/12.3/bin \ --new-bindir /usr/local/opt/postgresql@13/bin \ --old-datadir $HOME/.asdf/installs/postgres/12.3/data \ --new-datadir ./postgres/data \ --check Performing Consistency Checks ----------------------------- Checking cluster versions ok Checking database user is the install user ok Checking database connection settings ok Checking for prepared transactions ok Checking for system-defined composite types in user tables ok Checking for reg* data types in user tables ok Checking for contrib/isn with bigint-passing mismatch ok Checking for presence of required libraries ok Checking database user is the install user ok Checking for prepared transactions ok Checking for new cluster tablespace directories ok *Clusters are compatible* ``` If there is an issue, such as mismatched collation settings, the output will report the issue. You'll have to decide how to resolve those on a case-by-case basis. ================================================ FILE: postgres/check-if-the-local-server-is-running.md ================================================ # Check If The Local Server Is Running An install of PostgreSQL comes with a number of utilities including the `pg_isready` command. This command can be used to check if the local PostgreSQL server is up, running, and ready to receive connections. If the server has not yet been started, running the command will result in a `no response` response. ```bash $ pg_isready localhost:5432 - no response ``` In this case, the `pg_ctl` command can be used to start the server. ```bash $ pg_ctl -D $HOME/.asdf/installs/postgres/12.3/data start waiting for server to start.... ... done server started ``` It tells us that the server is started and we can confirm that by again running `pg_isready`. ```bash $ pg_isready localhost:5432 - accepting connections ``` This command is most useful as part of a script, such as in a CI environment. In that case, you may not want it writing to `stdout`, you just want to use the command's exit code. For that, you can tack on the `--quiet` flag. ``` $ pg_isready --quiet ``` [source](https://www.postgresql.org/docs/current/app-pg-isready.html) ================================================ FILE: postgres/check-if-user-role-exists-for-database.md ================================================ # Check If User Role Exists For Database User roles define who can access a database cluster and broadly what level of control they have over that cluster. The most straightforward way to check if a user role exists is to connect to one of the databases in the cluster and run a query against the `pg_roles` table. ```sql select * from pg_roles where rolename='dev'; rolname ------------ dev (1 row) ``` This same concept can be used in a script when automating some database setup. To do that, we'll use `-c` (and some other flags) to dispatch a query to `psql` from a shell context. ```bash psql postgres -tXAc "SELECT 1 FROM pg_roles WHERE rolname='dev'" \ | grep -q 1 \ || createuser --interactive dev ``` This queries for the value `1` if the user role named `dev` exists. The output of that is piped to `grep` (in quiet mode, `-q`) to check if `1` is in the output. If user roles doesn't exist and grep doesn't match on `1`, then the right side of the _or_ (`||`) gets called. That command could be whatever. I've chosen to call PostgreSQL's `createuser` to create the `dev` user role. ================================================ FILE: postgres/check-table-for-any-orphaned-records.md ================================================ # Check Table For Any Orphaned Records If you don't have a foreign key constraint in place to enforce the relationship between records in two different tables, then there are a number of ways you could end up with orphaned records. Orphaned records are records that have a value in an `*_id` column when that value doesn't correspond to any record in the related table. For example, let's say we have an `authors` table with an `id` column and a `books` table with an `author_id` column. If there is a book record with an `author_id` value that doesn't resolve to any record in the `authors` table, then that book is an orphaned record. You can find out if a table has orphaned records like so: ```sql select count(*) from books left join authors on books.author_id = authors.id where authors.id is null and books.author_id is not null; ``` We select from our table with the foreign key (`books`) and _left join_ it against the related table (`authors`). If there are any book records where the joined author row is `null`, then that book is orphaned. ================================================ FILE: postgres/check-the-size-of-databases-in-a-cluster.md ================================================ # Check The Size Of Databases In A Cluster The `\l` command in `psql` will list all the databases for the server. The field surfaced by this meta-command are: - Name - Owner - Encoding - Locale Provider - Collate - Ctype - ICU Locale - ICU Rules - Access privileges If we add a `+`, issuing instead `\l+`, we get three additional fields: - Size - Tablespace - Description The _Size_ column is the human-formatted size of each database. Another way to do this is with some SQL querying the underlying record keeping of the server's database. ```sql select db.datname as db_name, pg_size_pretty(pg_database_size(db.datname)) as db_size from pg_database db order by pg_database_size(db.datname) desc; ``` Credit to [this StackOverflow answer](https://stackoverflow.com/a/18907188/535590) for how to do this with a SQL query. [source](https://www.postgresql.org/docs/current/app-psql.html#APP-PSQL-META-COMMAND-LIST) ================================================ FILE: postgres/checking-inequality.md ================================================ # Checking Inequality In most languages there is a `!=` operator for checking inequality of two things. Postgres also supports the synonymous `<>` operator for checking inequality. ```sql > select 1 <> 1; ?column? ---------- f > select true <> false; ?column? ---------- t > select 'taco' <> 'burrito'; ?column? ---------- t ``` h/t Brian Dunn [source](https://www.postgresql.org/docs/9.5/static/functions-comparison.html) ================================================ FILE: postgres/checking-the-type-of-a-value.md ================================================ # Checking The Type Of A Value The `pg_typeof()` function allows you to determine the data type of anything in Postgres. ```sql > select pg_typeof(1); pg_typeof ----------- integer (1 row) > select pg_typeof(true); pg_typeof ----------- boolean (1 row) ``` If you try it on an arbitrary string, it is unable to disambiguate which string type (e.g. `text` vs `varchar`). ```sql > select pg_typeof('hello'); pg_typeof ----------- unknown (1 row) ``` You just have to be a bit more specific. ```sql > select pg_typeof('hello'::varchar); pg_typeof ------------------- character varying (1 row) ``` [source](http://www.postgresql.org/docs/9.3/static/functions-info.html#FUNCTIONS-INFO-CATALOG-TABLE) ================================================ FILE: postgres/clear-the-screen-in-psql-2.md ================================================ # Clear The Screen In psql (2) In [Clear The Screen In psql](clear-the-screen-in-psql.md), I showed how you can shell out to the `clear` command as a way of clearing the screen in `psql`. It turns out there is an even simpler way. Just hit `CTRL-l`. ================================================ FILE: postgres/clear-the-screen-in-psql.md ================================================ # Clear The Screen In psql The `psql` interactive terminal does not have a built-in way of clearing the screen. What I usually do if I really need the screen cleared is quit, run `clear` from the shell, and then open a new `psql` session. This is unnecessary though. Instead, we can use the `\!` command to execute a shell command, in this case, the `clear` command. ``` > \! clear ``` This clears the screen in one step and keeps our current session running. See the [psql docs](http://www.postgresql.org/docs/current/static/app-psql.html) for more details. ================================================ FILE: postgres/compute-hashes-with-pgcrypto.md ================================================ # Compute Hashes With pgcrypto The `pgcrypto` extension that comes with PostgreSQL adds access to some general hashing functions. Included are `md5`, `sha1`, `sha224`, `sha256`, `sha384` and `sha512`. Any of these hashing functions can be applied to an arbitrary string using the `digest` function. Here are example of the `md5` and `sha1` algorithms: ```sql > create extension pgcrypto; CREATE EXTENSION > select digest('Hello, World!', 'md5'); digest ------------------------------------ \x65a8e27d8879283831b664bd8b7f0ad4 > select digest('Hello, World!', 'sha1'); digest -------------------------------------------- \x0a0a9f2a6772942557ab5355d76af442f8f65e01 ``` See the [`pgcrypto` docs]( http://www.postgresql.org/docs/current/static/pgcrypto.html) for more details. ================================================ FILE: postgres/compute-median-instead-of-average.md ================================================ # Compute Median Instead Of Average One of the first aggregate functions we might use in PostgreSQL, besides `sum`, is `avg`. ```sql select avg(book_count) as average_books_read from ( select users.id, count(books.id) as book_count from users left join books on books.user_id = users.id where books.read_in_year = 2025 group by users.id ) as user_book_counts; ``` This computes the average of the set of values which sums them all up and divides by the count. The average (maybe you've heard this also called the _mean_) is not always the best way to understand data, especially when there are outliers. Instead, we might want to compute the _median_ value of our set of data. There is no easily identifiable `median` aggregate function. Instead, we can use `percentile_cont` with a value of `0.5`. This gets us the 50th percentile of our set of data which is the definition of the _median_. ```sql select percentile_cont(0.5) within group ( order by book_count ) as median_books_read from ( select users.id, count(books.id) as book_count from users left join books on books.user_id = users.id and books.read_in_year = 2025 group by users.id ) as user_book_counts; ``` The full syntax for `percentile_cont` is `percentile_cong(precision) within group (order by ...)` because this is an aggregiate that has to work with an ordered-set of data. [source](https://www.postgresql.org/docs/current/functions-aggregate.html) ================================================ FILE: postgres/compute-the-levenshtein-distance-of-two-strings.md ================================================ # Compute The Levenshtein Distance Of Two Strings PostgreSQL has a built-in function for computing the [Levenshtein distance](https://en.wikipedia.org/wiki/Levenshtein_distance) between two strings. ```sql > select levenshtein('hello', 'world'); levenshtein ------------- 4 > select levenshtein('function', 'funtcion'); levenshtein ------------- 2 ``` Check out the [`fuzzystrmatch` module](https://www.postgresql.org/docs/current/fuzzystrmatch.html#id-1.11.7.24.6) for more details. ================================================ FILE: postgres/compute-the-md5-hash-of-a-string.md ================================================ # Compute The md5 Hash Of A String One of the functions provided by PostgreSQL for working with string data is the `md5()` function. This function calculates the md5 hash of a given string. It works like this: ```sql > select md5('Hello, World!'); md5 ---------------------------------- 65a8e27d8879283831b664bd8b7f0ad4 > select md5('$3cr3tp4$$w0rd'); md5 ---------------------------------- bbabecfd4031211077473734bae7249f ``` There are more hashing algorithms provided by the `pgcrypto` extension. See [Compute Hashes With pgcrypto](postgres/compute-hashes-with-pgcrypto.md) for more details on that. ================================================ FILE: postgres/concatenate-strings-with-a-separator.md ================================================ # Concatenate Strings With A Separator I was putting together an example of using a generated column that concatenates string values from a few other columns. I used manual concatenation with the `||` operator like so: ```sql create table folders ( id integer generated always as identity primary key, user_id integer not null, name text not null, parent_folder_id integer references folders(id), path text generated always as ( user_id::text || ':' || lower(name) || ':' || coalesce(parent_folder_id::text, '0') ) stored ); ``` Instead of doing that manual concatenation for the `path` generated column, I can use [`concat_ws`](https://www.postgresql.org/docs/current/functions-string.html). ```sql create table folders ( id integer generated always as identity primary key, user_id integer not null, name text not null, parent_folder_id integer references folders(id), path text generated always as ( concat_ws( ':', user_id::text, lower(name), coalesce(parent_folder_id::text, '0') ) ) stored ); ``` The first argument to `concat_ws` is the separator I want to use. The remaining arguments are the strings that should be concatenated with that separator. One other things that is nice about `concat_ws` is that it will ignore `null` values that it receives. ```sql > select concat_ws(':', 'one', 'two', null, 'three'); +---------------+ | concat_ws | |---------------| | one:two:three | +---------------+ ``` ================================================ FILE: postgres/configure-the-timezone.md ================================================ # Configure The Timezone Running `show timezone;` will reveal the timezone for your postgres connection. If you want to change the timezone for the duration of the connection, you can run something like ``` > set timezone='America/New_York'; SET > show timezone; TimeZone ------------------ America/New_York (1 row) ``` Now, if you run a command such as `select now();`, the time will be in Eastern time. h/t Jack Christensen ================================================ FILE: postgres/constructing-a-range-of-dates.md ================================================ # Constructing A Range Of Dates PostgreSQL offers a number of range types including the `daterange` type. This can be constructed using the `daterange()` function with two strings representing the lower and upper bounds of the date range respectively. ```sql > select daterange('2015-1-1','2015-1-5'); daterange ------------------------- [2015-01-01,2015-01-05) ``` The lower bound is inclusive -- indicated by the `[` character -- and the upper bound is exclusive -- indicated by the `)` character. [source](http://www.postgresql.org/docs/current/static/rangetypes.html) ================================================ FILE: postgres/convert-a-string-to-a-timestamp.md ================================================ # Convert A String To A Timestamp If you have a string that represents a point in time, there are a couple ways that you can convert it to a PostgreSQL `timestamptz` value. If the string is in [ISO 8601 format](https://en.wikipedia.org/wiki/ISO_8601), then it can be simply cast to `timestamptz`. ```sql > select '2018-10-24'::timestamptz; timestamptz ------------------------ 2018-10-24 00:00:00-05 ``` A more general purpose approach is to use the [`to_timestamp`](https://www.postgresql.org/docs/11/static/functions-formatting.html) function. ```sql > select to_timestamp('2018-10-24', 'YYYY-MM-DD'); to_timestamp ------------------------ 2018-10-24 00:00:00-05 ``` The first argument is our string-to-be-converted in whatever format. The second argument is another string describing in what format that string is. Note: Both of these approaches produce a `timestamptz` value. ================================================ FILE: postgres/count-how-many-records-there-are-of-each-type.md ================================================ # Count How Many Records There Are Of Each Type Let's say I have a `books` table full of data. One of the columns on this table is `status` which represents whether the book is published, in review, or still a draft. We can find out how many records (books) there are for each `status` using a `group by` clause and the `count` aggregate function. ```sql > select status, count(*) from books group by status; status | count -----------+------- ø | 123 published | 611 draft | 364 review | 239 (4 rows) ``` Because we don't have a `not null` constraint on the `status` column, there are also some records that have a null value. We can take this a step further by ordering the output in a consistent way—descending order of the count column. ```sql > select status, count(*) from books group by status order by 2 desc; status | count -----------+------- published | 611 draft | 364 review | 239 ø | 123 (4 rows) ``` This `order by` clauses uses [a positional index from the select arguments](use-argument-indexes.md), so the `2` references the `count(*)` argument. ================================================ FILE: postgres/count-records-by-type.md ================================================ # Count Records By Type If you have a table with some sort of type column on it, you can come up with a count of the records in that table by type. You just need to take advantage of `group by`: ```sql > select type, count(*) from pokemon group by type; type | count ----------------- fire | 10 water | 4 plant | 7 psychic | 3 rock | 12 ``` ================================================ FILE: postgres/count-the-number-of-items-in-an-array.md ================================================ # Count The Number Of Items In An Array There are two ways to count the number of items in an array with PostgreSQL. The one that might jump out at you or show up at the top of search results is [`array_length`](https://www.postgresql.org/docs/current/functions-array.html). ```sql > select array_length(array[1,2,3], 1); +--------------+ | array_length | |--------------| | 3 | +--------------+ > select array_length(array[[1,2], [3,4]], 2); +--------------+ | array_length | |--------------| | 2 | +--------------+ ``` This requires specifying the dimension at which you want to check the length. The first example, checking the 1st dimension of a one-dimensional array, seems like the more common and useful scenario. In the second example, we are checking the 2nd dimension. The other way we can determine the number of items in an array is with the [`cardinality`](https://www.postgresql.org/docs/current/functions-array.html) function. > Returns the total number of elements in the array, or 0 if the array is > empty. ```sql > select cardinality(array[1,2,3]); +-------------+ | cardinality | |-------------| | 3 | +-------------+ > select cardinality(array[[1,2], [3,4]]); +-------------+ | cardinality | |-------------| | 4 | +-------------+ ``` This behaves the same as `array_length` for a one-dimensional array and doesn't require a second argument. Where it gets more interesting is with multi-dimensional arrays. It returns the total number of elements in the arrayregardless of the nesting. [source](https://mattrighetti.com/2025/01/20/you-dont-need-sql-builders) ================================================ FILE: postgres/count-the-number-of-trues-in-an-aggregate-query.md ================================================ # Count The Number Of Trues In An Aggregate Query The `sum` function is an aggregate function that allows you to sum up a bunch of integers. What if you want to sum up a boolean column? You may want to know how many times `true` appears in a collection of grouped records. This can be done by mixing in a `case` statement. ```sql select author_id, sum(case when available then 1 else 0 end) from books group by author_id; ``` Here, we are able to find out for each author how many books they have available. If we want to count `false` values, we can just invert the `sum` statement: ```sql sum(case when available then 0 else 1 end) ``` [source](https://stackoverflow.com/a/5396728/535590) ================================================ FILE: postgres/create-a-cluster-in-a-specific-data-directory.md ================================================ # Create A Cluster In A Specific Data Directory Let's say I want to create a PostgreSQL cluster near my app. So, I create a `postgres/data` directory next to my app. Then I run the `initdb` command pointing at that directory and specifying the UTF-8 locale. Here is what that looks like: ```bash $ /usr/local/opt/postgresql@13/bin/initdb -D postgres/data --locale=en_US.UTF-8 The files belonging to this database system will be owned by user "jbranchaud". This user must also own the server process. The database cluster will be initialized with locale "en_US.UTF-8". The default database encoding has accordingly been set to "UTF8". The default text search configuration will be set to "english". Data page checksums are disabled. fixing permissions on existing directory postgres/data ... ok creating subdirectories ... ok selecting dynamic shared memory implementation ... posix selecting default max_connections ... 100 selecting default shared_buffers ... 128MB selecting default time zone ... America/Chicago creating configuration files ... ok running bootstrap script ... ok performing post-bootstrap initialization ... ok syncing data to disk ... ok initdb: warning: enabling "trust" authentication for local connections You can change this by editing pg_hba.conf or using the option -A, or --auth-local and --auth-host, the next time you run initdb. Success. You can now start the database server using: '/usr/local/opt/postgresql@13/bin/pg_ctl' -D postgres/data -l logfile start ``` As stated at the end of the command's output, I can run the `postgres` server with that data directory with: ```bash $ '/usr/local/opt/postgresql@13/bin/pg_ctl' -D postgres/data -l logfile start ``` ================================================ FILE: postgres/create-a-composite-primary-key.md ================================================ # Create A Composite Primary Key The unique identifier for a given row in a table is the *primary key*. Generally, a row can be uniquely identified by a single data point (such as an id), so the primary key is simply that single data point. In some cases, your data can be more appropriately uniquely identified by multiple values. This is where composite primary keys can lend a hand. Consider an example `plane_tickets` table where each ticket can be uniquely identified by the passenger and flight it is associated with: ```sql create table plane_tickets ( passenger_id integer references passengers not null, flight_id integer references flights not null, confirmation_number varchar(6) not null, seat_assignment varchar not null, primary key (passenger_id, flight_id) ); ``` ================================================ FILE: postgres/create-a-table-from-the-structure-of-another.md ================================================ # Create A Table From The Structure Of Another There are a couple ways to create a new table from the structure of another table. One of those ways is with the [`create table as` syntax](https://www.postgresql.org/docs/current/sql-createtableas.html). ```sql create table dupe_table as table existing_table with no data; ``` I wouldn't recommend this approach though because it only reproduces the columns and datatypes. The modifiers, indexes, and constraints are not included. The [`create table` syntax](https://www.postgresql.org/docs/current/sql-createtable.html), on the other hand, gives you more options and flexibility for this kind of task. ```sql create table dupe_table (like existing_table); ``` This works just like the first statement, reproducing just the columns and datatypes. There are options for enhancing this statement. We can tell it to additionally _include_ things like `defaults`, `indexes`, `constraints`, or even just _everything_ (`including all`). Here is what it looks like to copy the `existing_table` so that things like `not null`, B-Tree indexes, and primary key `default` values are reproduced along with the columns and datatypes. ```sql create table dupe_table ( like existing_table including defaults including indexes including constraints ) ``` [source](https://www.reddit.com/r/PostgreSQL/comments/uu8xcs/comment/i9e36m2/) ================================================ FILE: postgres/create-an-index-across-two-columns.md ================================================ # Create An Index Across Two Columns Most commonly when we create an index, it is targeted at a single column of a table. Sometimes an expensive query that works with two different columns would be better off with an index that combines those two columns. This is called a _composite index_. Let's consider this query: ```sql select * from events where user_id = 123 order by created_at desc limit 1; ``` Though this query will use the index on `created_at` to do an Index Scan, it will still have to do a bunch of expensive filtering of `user_id` values after the fact. What this query needs to be efficient is a _composite index_ on `user_id` and `created_at`. We can create one like so: ```sql create index events_user_id_created_at_idx on events (user_id, created_at); ``` Instead of doing a bunch of post-index filtering on `user_id` values, that expensive query will factor `user_id` into its Index Scan and complete much quicker. See [the Postgres docs on multicolumn indexes](https://www.postgresql.org/docs/current/indexes-multicolumn.html) for more details. ================================================ FILE: postgres/create-an-index-without-locking-the-table.md ================================================ # Create An Index Without Locking The Table When creating an index for a column, the process of building the index will lock the column's table. For small datasets this isn't a concern because the index will take no time at all to create. For larger datasets, the lock could last long enough to create meaningful downtime. This can all be avoided by telling Postgres to build the index concurrently. ```sql create index concurrently idx_book_isbns on books(isbn); ``` Creating the index this way will take a bit longer and put more strain on machine resources, but it allows concurrent inserts, updates, or deletes on the table. In other words, you can add an index to a large table in a production environment without bringing down your app. Read more about the [details and potential caveats](https://www.postgresql.org/docs/current/sql-createindex.html#SQL-CREATEINDEX-CONCURRENTLY) in the docs. ================================================ FILE: postgres/create-and-execute-sql-statements-with-gexec.md ================================================ # Create And Execute SQL Statements With \gexec The [`\gexec` meta-command](https://www.postgresql.org/docs/current/app-psql.html#APP-PSQL-META-COMMAND-GEXEC) is a variation of the [`\g` meta-command](https://www.postgresql.org/docs/current/app-psql.html#APP-PSQL-META-COMMAND-G), both of which can be used in a `psql` session. Whereas the `\g` command sends the current query in the buffer to the PostgreSQL server for execution, the `\gexec` command first sends the query to the server for execution and then executes each row of the result as its own SQL statement. This is both a bit absurd and powerful. And a bit unnecessary considering all of the scripting capabilities with anything from bash to any language with a SQL client library. Nevertheless, let's take a look at a contrived example of how it works. Here, we have a SQL statement that does some string concatenation based off values in an array. This results in three separate `create schema` statements. ```sql > select 'create schema if not exists schema_' || letter || ';' from unnest(array['a', 'b', 'c']) as letter \gexec CREATE SCHEMA CREATE SCHEMA CREATE SCHEMA > \dn List of schemas Name | Owner ----------+------------------- public | pg_database_owner schema_a | postgres schema_b | postgres schema_c | postgres (4 rows) ``` Three new schemas get created which we can inspect with `\dn`. Notice, if we simply execute the primary statement, we can see the intermediate result that `\gexec` will subsequently execute. ```sql > select 'create schema if not exists schema_' || letter || ';' from unnest(array['a', 'b', 'c']) as letter \g ?column? --------------------------------------- create schema if not exists schema_a; create schema if not exists schema_b; create schema if not exists schema_c; (3 rows) ``` ================================================ FILE: postgres/create-database-uses-template1.md ================================================ # Create Database Uses Template1 Whenever you use the [`create database`](https://www.postgresql.org/docs/current/sql-createdatabase.html) query, unless otherwise specified, it will create it by cloning `template1` by default. You can view, inspect, and even modify this database template by connecting to it. ```sql \c template1 ``` Every Postgres cluster starts with two templates. ```sql select datname from pg_database where datistemplate = true; datname ----------- template1 template0 (2 rows) ``` You cannot however connect to and modify `template0`. It is a fallback clone of `template1` that you can utilize if you ever modify `template1` and need to restore it. [source](https://supabase.io/blog/2020/07/09/postgresql-templates/) ================================================ FILE: postgres/create-hstore-from-two-arrays.md ================================================ # Create hstore From Two Arrays PostgreSQL allows the use of the `hstore` type by enabling the `hstore` extension. One way to create an instance of `hstore` data is by passing two arrays to the `hstore()` function. The first array is a set of keys and the second array is the set of values. ```sql > select hstore(array['one','two','three'], array['1','2','3']); hstore -------------------------------------- "one"=>"1", "two"=>"2", "three"=>"3" ``` The two arrays must be the same length or an error will occur. ```sql > select hstore(array['one','two','three'], array['1','2']); ERROR: arrays must have same bounds ``` ================================================ FILE: postgres/create-table-adds-a-data-type.md ================================================ # Create Table Adds A Data Type Each time you create a table in PostgreSQL, a new data type represented by that table is created and added to the `pg_type` table. According to the Postgres docs: > CREATE TABLE also automatically creates a data type that represents the > composite type corresponding to one row of the table. Therefore, tables > cannot have the same name as any existing data type in the same schema. For instance, if you create a `users` table like so: ```sql create table users ( id serial primary key, first_name varchar not null, last_name varchar not null ); ``` then the `pg_type` will now contain an entry with a `typname` of `users`. ```sql select * from pg_type where typname = 'users'; -[ RECORD 1 ]--+------------ typname | users typnamespace | 2200 typowner | 16384 ... ``` h/t Bruce Momjian ================================================ FILE: postgres/creating-conditional-constraints.md ================================================ # Creating Conditional Constraints There are times when it doesn't make sense for a constraint to apply to all records in a table. For instance, if we have a table of pokemon, we may only want to apply a unique index constraint to the names of non-wild pokemon. This can be achieved with the following conditional constraint: ```sql create unique index pokemons_names on pokemons (names) where wild = false; ``` If we try to insert a non-wild pokemon with a duplicate name, we will get an error. Likewise, if we try to update a pokemon with a duplicate name from wild to non-wild, we will get an error. [source](http://www.postgresguide.com/performance/conditional.html) ================================================ FILE: postgres/creating-custom-types.md ================================================ # Creating Custom Types PostgreSQL has support for creating custom types. When you need something more expressive than the built-in types and you don't want your data spread across multiple columns, you can instead create a custom type. ```sql create type dimensions as ( width integer, height integer, depth integer ); ``` This new type can then be used in the definition of a new table ```sql create table moving_boxes ( id serial primary key, dims dimensions not null ); ``` and when inserting data ```sql insert into moving_boxes (dims) values (row(3,4,5)::dimensions); ``` See the [`create type` docs](http://www.postgresql.org/docs/current/static/sql-createtype.html) for more details. ================================================ FILE: postgres/day-of-week-by-name-for-a-date.md ================================================ # Day Of Week By Name For A Date In [Day Of Week For A Date](day-of-week-for-a-date.md), I explained how to determine what day of the week a date is as an integer with PostgreSQL. This used the `date_part()` function. By using the `to_char()` function with a date or timestamp, we can determine the day of the week by name (e.g. Monday). For instance, to determine what day today is, try a statement like the following: ```sql > select to_char(now(), 'Day'); to_char ----------- Sunday ``` The `Day` part of the second argument is just one of many template patterns that can be used for formatting dates and times. See [Data Type Formatting Functions](http://www.postgresql.org/docs/current/static/functions-formatting.html) in the Postgres docs for more details. ================================================ FILE: postgres/day-of-week-for-a-date.md ================================================ # Day Of Week For A Date Given a `date` in PostgreSQL ```sql > select '2050-1-1'::date; date ------------ 2050-01-01 ``` you can determine the day of the week for that date with the `date_part()` function ```sql > select date_part('dow', '2050-1-1'::date); date_part ----------- 6 ``` The days of week are `0` through `6`, `0` being Sunday and `6` being Saturday. [source](http://www.postgresql.org/docs/current/static/functions-datetime.html) ================================================ FILE: postgres/default-schema.md ================================================ # Default Schema Schemas can be used to organize tables within a database. You can see all the schemas your database has like so ```sql > select schema_name from information_schema.schemata; schema_name -------------------- pg_toast pg_temp_1 pg_toast_temp_1 pg_catalog public information_schema (6 rows) ``` When you create a new table, it will need to be placed under one of these schemas. So if you have a `create table posts (...)`, how does postgres know what schema to put it under? Postgres checks your `search_path` for a default. ```sql > show search_path; search_path ----------------- "$user", public (1 row) ``` From our first select statement, we see that there is no schema with my user name, so postgres uses public as the default schema. If we set the search path to something that won't resolve to a schema name, postgres will complain ```sql > set search_path = '$user'; SET > create table posts (...); ERROR: no schema has been selected to create in ``` ================================================ FILE: postgres/defining-arrays.md ================================================ # Defining Arrays In postgres, an array can be defined using the `array` syntax like so: ```sql > select array['a','b','c']; array --------- {a,b,c} ``` If you are inserting into an existing array column, you can use the array literal syntax. ```sql > create temp table favorite_numbers(numbers integer[]); CREATE TABLE > insert into favorite_numbers values( '{7,3,9}' ); INSERT 0 1 > select numbers[2] from favorite_numbers; numbers --------- 3 ``` Postgres also supports two-dimensional arrays. ```sql select array[[1,2,3],[4,5,6],[7,8,9]] telephone; telephone --------------------------- {{1,2,3},{4,5,6},{7,8,9}} ``` ================================================ FILE: postgres/determine-types-of-jsonb-records.md ================================================ # Determine Types Of JSONB Records You can stick several different things into a [JSONB postgres column](https://www.postgresql.org/docs/9.4/datatype-json.html). > Possible types are object, array, string, number, boolean, and null. If you are trying to audit what is in them, you might reach for: ```sql > select pg_typeof(my_jsonb_column) from my_table; ``` That is just gonna spit out `jsonb` over and over, like, I already know that. What you really want to know is, is the top-level thing an _object_, an _array_, or maybe just a _string_ or _number_. There are specific JSON processing functions for this, `json_typeof` and `jsonb_typeof` which you can call like so: ```sql > select jsonb_typeof(my_jsonb_column) from my_table; jsonb_typeof -------------- object array ... ``` [source](https://www.postgresql.org/docs/9.5/functions-json.html) ================================================ FILE: postgres/determining-the-age-of-things.md ================================================ # Determining The Age Of Things In PostgreSQL, we can determine the age of something (or someone) by passing a timestamp to the `age` function. For instance, if we want to know how long it has been since y2k, we can run the following query: ```sql > select age(timestamp '2000-01-01'); age ------------------------- 16 years 4 mons 12 days ``` Additionally, if we want to know the amount of time between two dates, we can pass two timestamps to the `age` function. For example, we can find out how old Prince lived to be by passing in the date of death and then date of birth: ```sql > select age(timestamp 'April 21, 2016', timestamp 'June 7, 1958'); age -------------------------- 57 years 10 mons 14 days ``` h/t Josh Davey ================================================ FILE: postgres/difference-between-explain-and-explain-analyze.md ================================================ # Difference Between Explain And Explain Analyze The `explain` statement allows you to gain some insight into the performance of a query. You may hear `explain` and `explain analyze` referred to interchangeably in conversation. Though they can both be used to explore how a query will perform, it's important to know a key difference. `explain analyze` executes the query, `explain` does not. For `select` queries, the distinction may not feel that important. For `insert`s, `update`s, and `delete`s, you'll want to be clear about which one you are using. ```sql > explain insert into books (title, author) values ('Fledgling', 'Octavia Butler'); QUERY PLAN ---------------------------------------------------- Insert on books (cost=0.00..0.01 rows=1 width=76) -> Result (cost=0.00..0.01 rows=1 width=76) > select count(*) from books; count ------- 0 ``` With `explain`, you get cost estimates of the `insert` statement. ```sql > explain analyze insert into books (title, author) values ('Fledgling', 'Octavia Butler'); QUERY PLAN ---------------------------------------------------------------------------------------------- Insert on books (cost=0.00..0.01 rows=1 width=76) (actual time=0.285..0.285 rows=0 loops=1) -> Result (cost=0.00..0.01 rows=1 width=76) (actual time=0.012..0.012 rows=1 loops=1) Planning time: 0.021 ms Execution time: 0.309 ms > select count(*) from books; count ------- 1 ``` With `explain analyze`, you get estimates and actual numbers. You also get a row inserted in the `books` table. ================================================ FILE: postgres/different-ways-to-define-an-interval.md ================================================ # Different Ways To Define An Interval There are several different ways in PostgreSQL to define an `interval` data type. An `interval` is useful because it can represent a discrete chunk of time. This is handy for doing date math. Here are four different ways to define an `interval`: 1. Use the `interval` keyword with a string ```sql > select interval '3 days'; interval ---------- 3 days (1 row) ``` 2. Cast a string to the `interval` type ```sql > select '3 days'::interval; interval ---------- 3 days (1 row) ``` 3. The `@` operator is a finicky syntax for declaring an interval ```sql > select @ 3 days; days ------ 3 (1 row) ``` 4. The [`make_interval` function](https://www.postgresql.org/docs/current/functions-datetime.html) can take various forms of arguments to construct an interval ```sql > select make_interval(days => 3); make_interval --------------- 3 days (1 row) ``` [source](https://www.postgresql.org/docs/current/datatype-datetime.html#DATATYPE-INTERVAL-INPUT) ================================================ FILE: postgres/dump-all-databases-to-a-sql-file.md ================================================ # Dump All Databases To A SQL File I recently needed to reinstall my local Postgres installation. I had several databases with data in that cluster that I wanted to preserve. Before I could go uninstalling and re-installing Postgres, I needed to dump the entire cluster of databases. The `pg_dumpall` command that installs with Postgres can be used for this. ```bash $ pg_dumpall > postgres_13_1_cluster_dump.sql ``` The command outputs to stdout a SQL dump of all the databases stored in the data directory of this Postgres instance. I took this a step further and ignored the `template0` and `template1` directories because I knew those would come with the new install. I did that by adding the `--exclude-database` flag with a pattern. ```bash $ pg_dumpall \ --exclude-database="template*" \ > postgres_13_1_cluster_dump.sql ``` This data dump can be restored with the new install using: ```bash $ psql -f postgres_13_1_cluster_dump.sql postgres ``` I wrote more about this process in [Reinstall Postgres with OpenSSL Using asdf](https://dev.to/jbranchaud/reinstall-postgresql-with-openssl-using-asdf-cmj). Also, see `pg_dumpall --help` or the [Postgres docs](https://www.postgresql.org/docs/current/app-pg-dumpall.html) for more details. ================================================ FILE: postgres/dump-and-restore-a-database.md ================================================ # Dump And Restore A Database PostgreSQL comes with two command-line utilities for dumping and then restoring a database -- `pg_dump` and `pg_restore`, respectively. Using the `pg_dump` with the `-Fc` flag will create a dump of the given database in a custom format. The output of this command can be redirected into a file (the `.dump` extension is a standard convention): ```bash $ pg_dump -Fc my_database > my_database.dump ``` Using the custom format option provides a couple benefits. The output is significantly compressed in comparison to a generic SQL dump. The dump and restoration is more flexible. Lastly, the dump can be performed in parallel if your machine has multiple cores to work with. Likewise, the restoration can be done in parallel with multiple jobs. To restore the dump, create a fresh database and then use `pg_restore`: ```bash $ createdb my_new_database $ pg_restore -d my_new_database my_database.dump ``` Note: the dumped tables will depend on some user role. You will need to ensure that this role exists on the database cluster where the restore is happening. You can use the `createuser` command if necessary. See the [`pg_dump` docs](http://www.postgresql.org/docs/current/static/app-pgdump.html) and [`pg_restore` docs](http://www.postgresql.org/docs/current/static/app-pgrestore.html) for more details. ================================================ FILE: postgres/dump-the-sql-needed-recreate-a-table.md ================================================ # Dump The SQL Needed To Recreate A Table The [`pg_dump`](https://www.postgresql.org/docs/current/app-pgdump.html) command and its arsenal of flags can do a lot of things. This includes producing the set of [DDL](https://www.postgresql.org/docs/current/ddl.html) SQL commands needed to recreate a table and all of it sequences, constraints, and indexes. The primary flags to know about for this scenario are `-t` (which lets you specify a table) and `--schema-only` (which indicates that we want to exclude data from the data). ```bash $ pg_dump -t 'users' --schema-only my_database > users.schema.sql ``` Run a command like to create a file `users.schema.sql` that will contain a series of SQL commands that will: - create the table with its columns (including defaults, `not null` constraints, etc.) - create and set the sequence on a serial ID column - add any foreign key constraints - create any indexes Then if you're ever wanting to recreate this table, you can hand that file directly to `pg_restore`. Or, since it is in SQL, you can run those commands manually. There are a ton of flags beyond the two covered here. Read about them in the [`pg_dump` docs pages](https://www.postgresql.org/docs/current/app-pgdump.html). ================================================ FILE: postgres/duplicate-a-local-database.md ================================================ # Duplicate A Local Database You can quickly create a new database instance that is a duplicate of another database. If the existing database is local, you don't need to dump and restore. Instead you can use the `createdb` command that comes with Postgres: ```bash $ createdb -O ownername -T originaldb newdb ``` This creates a new database called `newdb` using `originaldb` as a template (`-T`). This will include the entire schema and data of the original database. The `-O` flag allows you to specify the owner of the database. Since this is local, you probably want your primary unix user as the owner. [source](https://stackoverflow.com/a/6739995/535590) ================================================ FILE: postgres/edit-existing-functions.md ================================================ # Edit Existing Functions In the `psql` console, use `\ef` with the name of a function to fetch and open the definition of the function. The function will be opened in your system `$EDITOR` in the form of a `create or replace function` query. Executing ```sql > \ef now ``` will open the following in your default editor ```sql CREATE OR REPLACE FUNCTION pg_catalog.now() RETURNS timestamp with time zone LANGUAGE internal STABLE STRICT AS $function$now$function$ ``` ================================================ FILE: postgres/enable-logging-of-database-activity.md ================================================ # Enable Logging Of Database Activity For logging to be enabled for a PostgreSQL server, it needs to be properly configured. This means ensuring the `logging_collector` option is on. By default I believe it is `off`. This is configured in the `postgresql.conf` file and requires a server restart. First, to find where the conf file is. I can answer that question in a `psql` session. ```sql > show config_file; config_file --------------------------------------------------------------------- /Users/jbranchaud/.asdf/installs/postgres/12.3/data/postgresql.conf (1 row) ``` Now, I can open up that file and search for the line that has `logging_collector`. I uncomment that line and change `off` to `on`. ``` # This is used when logging to stderr: logging_collector = on # Enable capturing of stderr and csvlog # into log files. Required to be on for # csvlogs. # (change requires restart) ``` This requires a restart of the Postgres server. ```bash $ ~/.asdf/installs/postgres/12.3/bin/pg_ctl -D ~/.asdf/installs/postgres/12.3/data restart waiting for server to shut down.... done server stopped waiting for server to start... done server started ``` I can now adjust any further logging-related configurations on a server or session basis. And then view those logs. ================================================ FILE: postgres/enforce-uniqueness-on-column-expression.md ================================================ # Enforce Uniqueness On Column Expression When creating a table for, say `users`, where you will store `email` addresses, you'll likely want to enforce uniqueness on the that `email` field. And because people have all sorts of ways of entering their emails, in terms of casing, you may be tempted to try to enforce uniqueness on a lowercased version of `email`. ```sql create table users ( id integer generated always as identity primary key, email text not null, unique ( lower(email) ) -- !! this won't work ); ``` A unique _constraint_ must be on one or more columns. You cannot include an _expression_ when defining the unique constraint. You can however accomplish similar aims with [a _unique index_ on the expression](https://www.postgresql.org/docs/current/indexes-expressional.html). That is because the index is able to store the result of the expression in itself. ```sql create table users ( id integer generated always as identity primary key, email text not null ); create unique index unq_lower_email on users ( lower(email) ); ``` This is likely what you want for this example anyway because it will probably be a common query to look up the user by `lower(email)` and the index will speed up those queries. ================================================ FILE: postgres/escaping-a-quote-in-a-string.md ================================================ # Escaping A Quote In A String In PostgreSQL, string (`varchar` and `text`) literals are declared with single quotes (`'`). That means that any string containing a single quote will need some escaping. The way to escape a single quote is with another single quote. ```sql > select 'what''s up!'; ?column? ------------ what's up! ``` [source](http://jonathansacramento.com/posts/20160122-improve-postgresql-workflow-vim-dbext.html) ================================================ FILE: postgres/escaping-string-literals-with-dollar-quoting.md ================================================ # Escaping String Literals With Dollar Quoting String literals in PostgreSQL are defined by surrounding the content with the `'` character. For string literals that contain the `'` character, you may have seen it escaped with a preceding `'`. ```sql > select 'Isn''t this nice?'; ?column? ------------------ Isn't this nice? ``` This is easy enough to do, but can be error prone and doesn't work well if SQL is being programmatically generated. A great workaround is to escape string literals using what is called dollar quoting. ```sql > select $$Isn't this even nicer?$$; ?column? ------------------------ Isn't this even nicer? ``` Just wrap both ends in `$$` instead of `'`. [source](https://www.postgresql.org/docs/current/static/sql-syntax-lexical.html) ================================================ FILE: postgres/export-query-results-to-a-csv.md ================================================ # Export Query Results To A CSV Digging through the results of queries in Postgres's `psql` is great if you are a programmer, but eventually someone without the skills or access may need to check out that data. Exporting the results of a query to CSV is a friendly way to share said results because most people will have a program on their computer that can read a CSV file. For example, exporting all your pokemon to `/tmp/pokemon_dump.csv` can be accomplished with: ```sql copy (select * from pokemons) to '/tmp/pokemon_dump.csv' csv; ``` Because we are grabbing the entire table, we can just specify the table name instead of using a subquery: ```sql copy pokemons to '/tmp/pokemon_dump.csv' csv; ``` Include the column names as headers to the CSV file with the `header` keyword: ```sql copy (select * from pokemons) to '/tmp/pokemon_dump.csv' csv header; ``` If your user has limited access, you can use the \copy command like so: ```sql \copy (select * from pokemons) to '/tmp/pokemon_dump.csv' with csv header; ``` [source](http://stackoverflow.com/questions/1120109/export-postgres-table-to-csv-file-with-headings) ================================================ FILE: postgres/extracting-nested-json-data.md ================================================ # Extracting Nested JSON Data If you are storing nested JSON data in a postgres JSON column, you are likely going to find yourself in a situation where you need to access some of those nested values in your database code. For instance, you may need to get at the license number in this JSON column ```sql owner -------------------------------------------------------------------------------- '{ "name": "Jason Borne", "license": { "number": "T1234F5G6", "state": "MA" } }' ``` Unfortunately, the `->` operator isn't going to do the trick. You need the `json_extract_path` function ```sql > select json_extract_path(owner, 'license', 'number') from some_table; json_extract_path ------------------- 'T1234F5G6' ``` Read more about [JSON Functions and Operators](http://www.postgresql.org/docs/9.4/static/functions-json.html). ================================================ FILE: postgres/fetch-data-from-an-endpoint-in-sql.md ================================================ # Fetch Data From An Endpoint In SQL The [`pgsql-http` extension](https://github.com/pramsey/pgsql-http) provides a variety of functions for allowing PostgreSQL to act as an HTTP client. This is a bit unorthodox and may not be a good idea in production systems. That said, it is cool that it is possible. Let's look at an example of it. First, I've installed the extension on the Docker container running my local Postgres server. ```bash $ docker exec -it still-postgres-1 bash $ apt-get update $ apt-get install postgres-16-http # I'm running Postgres v16 $ exit ``` Then I'll connect to a `psql` session in that container for the `postgres` database. ```bash $ docker exec still-postgres-1 psql -U postgres -d postgres ``` Then I enable the extension. ```sql > create extension if not exists http; CREATE EXTENSION ``` Now I can point a PostgreSQL statement at a live endpoint like [https://httpbun.com/ip](https://httpbun.com/ip) which will respond with a chunk of JSON including the IP address for that project's server. I do this using `http_get` which makes a `GET` request to the given endpoint. The body is included in the result set. ```bash > select content from http_get('http://httpbun.com/ip'); content ----------------------------- { + "origin": "73.75.236.101"+ } + (1 row) ``` ================================================ FILE: postgres/fetch-specific-number-of-results.md ================================================ # Fetch Specific Number Of Results If you pull up just about any intro to PostgreSQL (or even SQL), one of the first things they are going to teach you is the `limit` clause. This is taught as _the_ way for limiting the result set to a specific number of rows. ```sql > select title from books limit 4; +-----------------------+ | title | |-----------------------| | The Secret History | | A Gentleman in Moscow | | Exhalation: Stores | | Annihilation | +-----------------------+ SELECT 4 ``` You might be as surprised as I was to learn that `limit` is not part of the SQL standard. It is extremely common for this use case, but the SQL standard defines `fetch first N rows only` as the way to fetch a specific number of rows. As we can see, [it works identically to `limit N`](https://www.postgresql.org/docs/current/sql-select.html#SQL-LIMIT). ```sql > select title from books fetch first 4 rows only; +-----------------------+ | title | |-----------------------| | The Secret History | | A Gentleman in Moscow | | Exhalation: Stores | | Annihilation | +-----------------------+ SELECT 4 ``` The `rows` and `row` keywords are interchangeable which makes statements more readable if, for instance, you're doing `... fetch first 1 row only`. [source](https://www.cybertec-postgresql.com/en/postgresql-limit-vs-fetch-first-rows-with-ties/) ================================================ FILE: postgres/find-duplicate-records-in-table-without-unique-id.md ================================================ # Find Duplicate Records In Table Without Unique Id I recently came across a couple methods for listing out instances of duplicate records in a table where the table doesn't have an explicit unique identifier. Here is [a post](find-records-that-contain-duplicate-values.md) that explains how to do this when a unique identifier is present. If the table doesn't have an explicit primary key or other uniquely identifying value, then we'll have to get some help from [PostgreSQL's internal system columns](https://www.postgresql.org/docs/current/ddl-system-columns.html) — namely the `ctid`. The `ctid` is: > The physical location of the row version within its table. Let's use the example of the `mailing_list` table with potential duplicate `email` values. Here is the [first approach](https://stackoverflow.com/a/26773018/535590): ```sql delete from mailing_list where ctid not in ( select min(ctid) from mailing_list group by email ); ``` This uses a subquery to find the first occurrence of every unique email and then deletes the rest. The `ctid` is the unique value that we can call the `min` aggregate on. A [second approach](https://stackoverflow.com/a/46775289/535590): ```sql delete from mailing_list ml1 using mailing_list ml2 where ml1.ctid < ml2.ctid and ml1.email = ml2.email; ``` This uses `delete using` to join the table against itself as a cartesian product to compare every entry to every other entry. ================================================ FILE: postgres/find-records-that-contain-duplicate-values.md ================================================ # Find Records That Contain Duplicate Values Let's say I have a `mailing_list` table that contains all the email addresses that I want to send a mailing out to. Without a uniqueness constraint on the `email` column, I can end up with multiple records containing the same email address — duplicates. Here are a couple queries for checking to see if any duplicate records exist and which ones they are. ```sql select email from ( select email, row_number() over ( partition by email order by email ) as row_num from mailing_list ) t where t.row_num > 1; ``` This is cool because it uses a [window function](https://www.postgresql.org/docs/current/tutorial-window.html), specifically the [`row_number()`](https://www.postgresql.org/docs/current/functions-window.html) window function, to assign an incrementing number to each row in the partition. Here is another, conceptually simpler approach. ```sql select email count(*) from mailing_list group by email having count(*) > 1 order by email; ``` Though we cannot use a `where` clause with an aggregate (`count`), we can reach for a `having` clause to grab only those results where we've found more than `1` — duplicates. [source](https://www.postgresqltutorial.com/how-to-delete-duplicate-rows-in-postgresql/) ================================================ FILE: postgres/find-records-that-have-multiple-associated-records.md ================================================ # Find Records That Have Multiple Associated Records A common type of table association in a relational database is a one-to-many relationship. For instance, a database representing a bookshelf may have an `authors` table where each record can be associated with multiple records in the `books` table. That relationship is represented by a `author_id` foreign key column on `books` that points to `authors.id`. We can write a query to find all authors that have not zero or one, but multiple books by doing a join and then tacking on a `having` clause. ```sql select authors.id, authors.name, count(books.id) from authors join books on authors.id = books.author_id group by authors.id having count(books.id) >= 2; ``` This will result in a listing of author ids, author names, and their number of books. It does this by joining books to authors, grouping by the `authors.id` to produce a set of records unique to each author, and then combining multiple books by aggregating them with a `count`. The `having` clause is necessary because it is our way of _filtering_ on an aggregate value, in this case the `count`. ================================================ FILE: postgres/find-the-data-directory.md ================================================ # Find The Data Directory Where does postgres store all of the data for a database cluster? Well, in its data directory. Where exactly that data directory is can depend on how the database cluster was setup. Postgres can tell you right where to look, though. Within `psql`, run ```sql > show data_directory ``` Postgres will output the absolute path of the data directory. [source](http://dba.stackexchange.com/questions/1350/how-do-i-find-postgresqls-data-directory) ================================================ FILE: postgres/find-the-location-of-postgres-config-files.md ================================================ # Find The Location Of Postgres Config Files If you can connect to your database with `psql`, then you can easily find the location of your Postgres config files. After connecting, I can ask Postgres to show me where the main config file is: ```sql > show config_file; config_file -------------------------------------------------------------------------------- /Users/jbranchaud/Library/Application Support/Postgres/var-9.5/postgresql.conf ``` In the same directory as that `postgresql.conf` file are a number of other configuration files such as the `pg_hba.conf` file. h/t Dillon Hafer ================================================ FILE: postgres/fizzbuzz-with-common-table-expressions.md ================================================ # Fizzbuzz With Common Table Expressions In learning about CTEs (common table expressions) in postgres, I discovered that you can do some interesting and powerful things using the `with recursive` construct. The following solves the fizzbuzz problem for integers up to 100 ```sql with recursive fizzbuzz (num,val) as ( select 0, '' union select (num + 1), case when (num + 1) % 15 = 0 then 'fizzbuzz' when (num + 1) % 5 = 0 then 'buzz' when (num + 1) % 3 = 0 then 'fizz' else (num + 1)::text end from fizzbuzz where num < 100 ) select val from fizzbuzz where num > 0; ``` Check out [With Queries (Common Table Expressions)](http://www.postgresql.org/docs/9.4/static/queries-with.html) for more details on CTEs. ================================================ FILE: postgres/force-ssl-when-making-a-psql-connection.md ================================================ # Force SSL When Making A psql Connection If you try connecting `psql` to a Postgres server that requires SSL connections, you'll see an error message like this: ``` psql: error: FATAL: SSL/TLS required ``` You can tell `psql` to make an SSL connection by adding the `sslmode=require` key-value pair to the connection. If you're using a connection string, that might look like this: ```bash $ psql postgres://username:password@host.com/dbname?sslmode=require ``` Or if you're using the connection parameter flags, it may look like this, using the `--set` flag: ```bash $ PGPASSWORD=password psql \ --set=sslmode=require \ -h host.com \ -U username \ dbname ``` If after adding `sslmode=require`, you find that SSL support is not compiled in, [follow the instructions in this post to reinstall Postgres with SSL support](https://dev.to/jbranchaud/reinstall-postgresql-with-openssl-using-asdf-cmj). ================================================ FILE: postgres/generate-a-uuid.md ================================================ # Generate A UUID Postgres has support for universally unique identifiers (UUIDs) as a column data type via `uuid`. If you have a UUID column, you may need to generate a UUID. This requires the `uuid-ossp` module. This module provides a number of functions for generating UUIDs including the `uuid_generate_v4()` function which bases the UUID entirely off random numbers. ```sql > create extension "uuid-ossp"; CREATE EXTENSION > select uuid_generate_v4(); uuid_generate_v4 -------------------------------------- a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11 ``` See the [postgres docs](http://www.postgresql.org/docs/9.4/static/uuid-ossp.html) for more details on UUID generation functions. ================================================ FILE: postgres/generate-modern-primary-key-columns.md ================================================ # Generate Modern Primary Key Columns Chances are if you have looked at some examples, blog posts, or real-world instances of a `create table` statement, it defined the primary key with `serial` (or `bigserial`). ```sql create table books ( id serial primary key, title text not null, author text not null, created_at timestamptz not null default now(), updated_at timestamptz not null default now() ); ``` The `serial` syntax is everywhere, but for quite a while now it has not been the recommended way to define a primary key column for the `int` or `bigint` data types. The ["Don't Do This" page of the PostgreSQL wiki](https://wiki.postgresql.org/wiki/Don%27t_Do_This#Don.27t_use_serial) says "Don't use serial". > For new applications, identity columns should be used instead. The serial > types have some weird behaviors that make schema, dependency, and permission > management unnecessarily cumbersome. The modern way to define a primary key column for `int` or `bigint` is with a generated identity column. ```sql create table books ( id int primary key generated always as identity, title text not null, author text not null, created_at timestamptz not null default now(), updated_at timestamptz not null default now() ); ``` Check out the PostgreSQL docs for more about [identity columns](https://www.postgresql.org/docs/17/ddl-identity-columns.html). ================================================ FILE: postgres/generate-random-alphanumeric-identifier.md ================================================ # Generate Random Alphanumeric Identifier Here is a PostgreSQL query that uses [`pgcrypto`](https://www.postgresql.org/docs/current/pgcrypto.html) (for [`get_random_bytes`](https://www.postgresql.org/docs/current/pgcrypto.html#PGCRYPTO-RANDOM-DATA-FUNCS)) and a CTE to generate a cryptographically-random 8-character alphanumeric identifier. ```sql -- First ensure pgcrypto is installed create extension if not exists pgcrypto; -- Generates a single 8-character identifier with chars as ( -- excludes some look-alike characters select '23456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnpqrstuvwxyz' as charset ), random_bytes as ( select gen_random_bytes(8) as bytes ), positions as ( select generate_series(0, 7) as pos ) select string_agg( substr( charset, (get_byte(bytes, pos) % length(charset)) + 1, 1 ), '' order by pos ) as short_id from positions, random_bytes, chars; ``` Here is an example of the output: ```sql +----------+ | short_id | |----------| | NXdu9AnV | +----------+ ``` The [`generate_series`](https://www.postgresql.org/docs/current/functions-srf.html) gives us an 8-row table from 0 to 7 that we can use as indexes into the byte positions of the value we get from `gen_random_bytes`. Those random bytes get mapped to individual alphanumeric characters from `chars`. That then gets squeezed together with `string_agg`. Note: the character set excludes some characters that can be mistaken for one another like `0` and `O` or `1` and `l`. Note: you could change the right-bound of the `generate_series` to generate a different length identifier. ================================================ FILE: postgres/generate-random-uuids-without-an-extension.md ================================================ # Generate Random UUIDs Without An Extension In other posts I've covered how to generate v4 random UUIDs in PostgreSQL [using the `uuid-ossp` extension](generate-a-uuid.md) as well as the more up-to-date method of [using the `pgcrypto` extension](generating-uuids-with-pgcrypto.md). As of PostgreSQL v13, you no longer need to add an extension for v4 UUID generation. It comes built-in as the `gen_random_uuid()` function. ```sql > select gen_random_uuid(); gen_random_uuid -------------------------------------- 0aa72fe6-ede7-4ccf-b328-348becc58066 (1 row) ``` If you need other non-v4 UUID functions, you'll have to stick with [uuid-ossp](https://www.postgresql.org/docs/current/uuid-ossp.html). ================================================ FILE: postgres/generate-series-of-numbers.md ================================================ # Generate Series Of Numbers Postgres has a `generate_series` function that can be used to, well, generate a series of something. The simplest way to use it is by giving it `start` and `stop` arguments ```sql > select generate_series(1,5); generate_series ----------------- 1 2 3 4 5 ``` The default step is 1, so if you want to count backwards, you need to specify a negative step ```sql > select generate_series(5,1,-1); generate_series ----------------- 5 4 3 2 1 ``` You can use a larger step value to, for instance, get only multiples of 3 ```sql > select generate_series(3,17,3); generate_series ----------------- 3 6 9 12 15 ``` Trying this out with timestamps is left as an exercise for the reader. ================================================ FILE: postgres/generating-uuids-with-pgcrypto.md ================================================ # Generating UUIDs With pgcrypto If you check out the docs for the [`uuid-ossp` extension](https://www.postgresql.org/docs/current/static/uuid-ossp.html), you'll come across the following message. > The OSSP UUID library... is not well maintained, and is becoming > increasingly difficult to port to newer platforms. A little bit later, it says: > If you only need randomly-generated (version 4) UUIDs, consider using the > gen_random_uuid() function from the pgcrypto module instead. So, if we are using the UUID data type and only need to generate random UUIDs, we can rely on the [`pgcrypto` extension](https://www.postgresql.org/docs/current/static/pgcrypto.html). It comes with the `gen_random_uuid()` function which generates random v4 UUIDs. ```sql > create extension "pgcrypto"; CREATE EXTENSION > select gen_random_uuid(); gen_random_uuid -------------------------------------- 0a557c31-0632-4d3e-a349-e0adefb66a69 > select gen_random_uuid(); gen_random_uuid -------------------------------------- 83cdd678-8198-4d56-935d-d052f2e9db37 ``` ================================================ FILE: postgres/get-a-quick-approximate-count-of-a-table.md ================================================ # Get A Quick Approximate Count Of A Table Really large PostgreSQL tables can be slow to work with. Even a count of the rows in a really large table can take a while to tabulate. I'm talking about tables on the order of hundreds of millions of rows. For instance, here is a query grabbing the count of a ~400 million row table. ```sql > select count(*) from events; count ----------- 427462316 (1 row) Time: 55113.794 ms ``` If I'm willing to wait nearly a minute (55 seconds), I can get an accurate count of the rows in this `events` table. If I don't want to wait and an approximate count will do, there are faster ways. One way is to query the `pg_class` table. ``` > select reltuples::numeric as count from pg_class where relname='events'; count ----------- 427462000 (1 row) Time: 0.413 ms ``` The resulting count is within hundreds of the actual value and tells me what I need to know. And instead of 55 seconds, it takes less than half a millisecond. [source](https://andyatkinson.com/postgresql-tips) ================================================ FILE: postgres/get-row-count-for-most-recent-query.md ================================================ # Get Row Count For Most Recent Query Anytime you execute a query in `psql`, there is a _row count_ associated with that query. This is most naturally understood with a `select` query where a discreet number of rows are returned. We typically see the row count (e.g. `(19 rows)`) right below the result set. You can always reference the row count of the most recent query with [the `:ROW_COUNT` variable](https://www.postgresql.org/docs/current/app-psql.html#APP-PSQL-VARIABLES-ROW-COUNT). Here we use `\echo` to print it out. ```sql > select generate_series(2,20); generate_series ----------------- 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 (19 rows) Time: 12.338 ms > \echo :ROW_COUNT 19 ``` For some queries, like one that induces a pager (e.g. `less`) to be used, you'll lose track of the row count once the pager closes. This is where being able to reference the row count without rerunning the query is most useful. ```sql > select generate_series(2,2000); Time: 9.815 ms > \echo :ROW_COUNT 1999 ``` Notice, we can also get a row count from other kinds of queries like this `insert` statement. ```sql > insert into users (id) values (50001), (50002), (50003); INSERT 0 3 Time: 2.804 ms > \echo :ROW_COUNT 3 ``` [source](https://postgresql.verite.pro/blog/2024/05/13/advanced-psql-coproc.html) ================================================ FILE: postgres/get-the-size-of-a-database.md ================================================ # Get The Size Of A Database If you have connect access to a PostgreSQL database, you can use the `pg_database_size()` function to get the size of a database in bytes. ```sql > select pg_database_size('hr_hotels'); pg_database_size ------------------ 8249516 ``` Just give it the name of the database and it will tell you how much disk space that database is taking up. Checkout [the Postgres docs](http://www.postgresql.org/docs/current/static/functions-admin.html) for more details. ================================================ FILE: postgres/get-the-size-of-a-table.md ================================================ # Get The Size Of A Table In [Get The Size Of A Database](get-the-size-of-a-database.md), I showed a PostgreSQL administrative function, `pg_database_size()`, that gets the size of a given database. With the `pg_relation_size()` function, we can get the size of a given table. For instance, if we'd like to see the size of the `reservations` table, we can executing the following query: ```sql > select pg_relation_size('reservations'); pg_relation_size ------------------ 1531904 ``` This gives us the size of the `reservations` table in bytes. As you might expect, the referenced table needs to be part of the connected database and on the search path. See [the Postgres docs](http://www.postgresql.org/docs/current/static/functions-admin.html) for more details. ================================================ FILE: postgres/get-the-size-of-an-index.md ================================================ # Get The Size Of An Index Want to get an idea of how much disk space that additional index is taking up? You can query for it with the same methods discussed in [Get The Size Of A Table](get-the-size-of-a-table.md) and [Pretty Print Data Sizes](pretty-print-data-sizes.md). For instance, if I have a table with a `users_pkey` index and a `users_unique_lower_email_idx` index, I can check the sizes like so: ```sql > select pg_size_pretty(pg_relation_size('users_pkey')); pg_size_pretty ---------------- 240 kB > select pg_size_pretty(pg_relation_size('users_unique_lower_email_idx')); pg_size_pretty ---------------- 704 kB ``` [source](https://www.niwi.nz/2013/02/17/postgresql-database-table-indexes-size/) ================================================ FILE: postgres/get-the-size-on-disk-of-an-index.md ================================================ # Get The Size On Disk Of An Index Indexes, when added to the right columns, can provide massive performance gains for certain queries. Indexes aren't free though. It is worth noting that they take up disk space. The amount of disk space they take is generally irrelevant, but it is at least worth being aware of. Especially if you're deal with a massive table. You can check the current size of an index on disk with [PostgreSQL's `pg_relation_size` function](https://www.postgresql.org/docs/current/functions-admin.html). First, you'll want to look up the name of the index you're curious about. Running `\d table_name` (replacing `table_name` with the name of the table the index is on) will show you everything about the table including its indexes and their names. Then run the following query: ```sql select pg_size_pretty(pg_relation_size('index_users_on_email')); pg_size_pretty ---------------- 41 MB (1 row) ``` This one is pretty small. They can get pretty big though. I've seen some that take up over 1GB on disk. ================================================ FILE: postgres/getting-a-slice-of-an-array.md ================================================ # Getting A Slice Of An Array Postgres has a very natural syntax for grabbing a slice of an array. You simply add brackets after the array declaring the lower and upper bounds of the slice separated by a colon. ```sql > select (array[4,5,6,7,8,9])[2:4]; array --------- {5,6,7} ``` Notice that the bounds are inclusive, the array index is `1`-based, and the array declaration above needs to be wrapped in parentheses in order to not trip up the array slice syntax. You can also select rectangular slices from two dimensional arrays like so: ```sql > select (array[[1,2,3],[4,5,6],[7,8,9]])[2:3][1:2]; array --------------- {{4,5},{7,8}} ``` ================================================ FILE: postgres/group-by-the-result-of-a-function-call.md ================================================ # Group By The Result Of A Function Call Typically, a query that I write involving a `group by` will look more or less like this: ```sql select category, count(*) from products group by category; ``` The `category` column is the thing I'm grouping by. In this case, I'm doing a little data exploration. We are not strictly limited to grouping by a column. We can use all sorts of functions offered by Postgres to get at more interesting results. [String functions](https://www.postgresql.org/docs/current/functions-string.html) are a great place to start. Let's say our `products` table also has an `identifier` column with a naming scheme where the first three letters of the identifier correspond to the product's classification. We can group by that part of the `identifier`: ```sql select substring(identifier from 1 for 3), count(*) from products group by substring(identifier from 1 for 3); ``` The funkiness of the `substring` syntax aside, we were able to group our products in a new way and learn something about our data. ================================================ FILE: postgres/idempotent-inserts.md ================================================ # Idempotent Inserts I'm writing a bunch of records from one table to another using [`insert`](https://www.postgresql.org/docs/current/sql-insert.html). This is part of a process to swap out a massive table with an identical version that has a fraction of the records. I want to copy a subset of rows from the primary table to the replacement table that meet certain criteria. This query should be re-runnable and idempotent. That way I can run it, investage the replacement table, and then run it again before swapping out the tables. This helps ensure I don't miss anything. Such an idempotent query can be written with the help of the `on conflict` clause. ```sql insert into replacement_table select * from original_table where created_at > (now() - '3 months'::interval) on conflict do nothing; ``` If I run that multiple times, it skips over any records that would otherwise cause a conflict. The unique primary key column is usually what determines a conflicting record. Without the `on conflict do nothing`, trying to run this `insert` multiple times will fail on subsequent runs when it tries to re-insert rows with primary keys it has already inserted. ================================================ FILE: postgres/include-all-queries-in-the-log-file.md ================================================ # Include All Queries In The Log File The default log-level (`log_statement` setting) for a PostgreSQL server is `none`. Other valid log-levels for [that setting are `ddl`, `mod`, and `all`](https://www.postgresql.org/docs/13/runtime-config-logging.html). If you want to see all the queries hitting a database, you'll want to set it to `all`. This can be set as a server-wide setting or it can be set on a per-session basis. Because `all` is so noisy, I like to use it on a per-session basis when I'm in a situation where I know I'd like to see all queries. ```sql > set log_statement = 'all'; ``` After running that statement in my `psql` session, I can tail the log file to keep an eye on queries hitting the database. Be sure that [logging is enabled via the `logging_collector`](enable-logging-of-database-activity.md) as well. ================================================ FILE: postgres/include-columns-in-a-covering-index.md ================================================ # Include Columns In A Covering Index A _covering index_ is a special type of B-Tree index that, in addition to indexing on a certain field, also _includes_ one or more columns as extra data in the leaves of the tree. When created correctly, this can speed up the queries it targets by achieving an _index-only scan_. Let's say we have a frequently run query on a large `events` table that looks like this: ```sql select user_id, identifier, type from events where user_id = $1; ``` Here is what it looks like to create an index for this query with the `include` keyword: ```sql create index user_id_on_events_idx on (user_id) include (identifier, type); ``` An index on its own can already cause a significant speed up to the queries it targets, but may still need to retrieve some `select` attributes from the table. For hot-path queries with a set of specific columns always included in the select, there can be significant additional speed ups by having the index _cover_ those columns. For more details, check out [A Close Look At The Index Include Clause](https://use-the-index-luke.com/blog/2019-04/include-columns-in-btree-indexes). ================================================ FILE: postgres/include-multiple-tables-in-a-pg-dump.md ================================================ # Include Multiple Tables In A pg_dump When the `pg_dump` command is given the `-t` flag, it will dump just the table named with that flag. If you want to include multiple tables in the dump, you just need to use the flag multiple times. ```bash $ pg_dump -t users -t users_roles -t roles my_database > roles.dump.sql ``` Alternatively, you can specify a [pattern](https://www.postgresql.org/docs/current/app-psql.html#APP-PSQL-PATTERNS) when using the `-t` flag. ```bash $ pg_dump -t 'users*|roles' my_database > roles.dump.sql ``` You have to be a little more mindful of what will and won't be included when crafting a pattern. It is a nice shortcut for a well-known or well-constrained data model. See the [`pg_dump` docs](https://www.postgresql.org/docs/current/app-pgdump.html) for more details, as well as [some examples](https://www.postgresql.org/docs/current/app-pgdump.html#PG-DUMP-EXAMPLES). ================================================ FILE: postgres/insert-a-bunch-of-records-with-generate-series.md ================================================ # Insert A Bunch Of Records With Generate Series Sometimes you want to quickly insert a bunch of fake (or real) data into a Postgres table. This is a great way to populate seed data or set up data scenarios for testing things out. For instance, I recently used the following query to generate a bunch of data in a `roles` table to test out a query performance issue. ```sql > insert into roles ( name, resource_id, resource_type, created_at, updated_at ) select 'organization_user', g.id, 'Organization', now(), now() from generate_series(1,54000) as g(id); ``` The key part is the [`generate_series()` function](https://www.postgresql.org/docs/current/functions-srf.html) which will produce a row for every value between the two arguments. In this case, it will generate `1`, `2`, `3`, etc. all the way up to `54000` -- so 54k rows in total. The query selects off that generated series with some static values and some timestamps to create sufficiently fake data that can be inserted into the specified columns of the `roles` table. This quickly inserts tens of thousands of records that I can now use to test out the performance of a SQL query. ================================================ FILE: postgres/insert-just-the-defaults.md ================================================ # Insert Just The Defaults If you are constructing an `INSERT` statement for a table whose required columns all have default values, you may just want to use the defaults. In this situation, you can break away from the standard: ``` > insert into table_name (column1, column2) values (value1, value2); ``` Instead, simply tell Postgres that you want it to use the default values: ``` > insert into table_name default values; ``` ================================================ FILE: postgres/inspect-progress-of-long-running-create-index.md ================================================ # Inspect Progress Of Long-Running Create Index Strategically applied indexes are an important part of keeping queries against a database fast. Initially applying those indexes—especially for large tables in production—can take a bit of time. The `create index` call doesn't provide any indication of progress. So if applying an index takes minutes or even hours, it can be disconcerting. Is it still working or is it locked up? How far along is it? Postgres tracks the index creation process in `pg_stat_progress_create_index` and we can query that table. ```sql select now()::time(0), a.query, p.phase, p.blocks_total, p.blocks_done, p.tuples_total, p.tuples_done, ai.schemaname, ai.relname, ai.indexrelname from pg_stat_progress_create_index p join pg_stat_activity a on p.pid = a.pid left join pg_stat_all_indexes ai on ai.relid = p.relid and ai.indexrelid = p.index_relid; ``` There are a bunch of phases that Postgres goes through to create the index, especially if it is being created `concurrently`. The `blocks_done` and `tuples_done` numbers will keep ticking along, giving you an indication that the process is proceeding. [source one](https://dba.stackexchange.com/a/249784) and [source two](https://www.depesz.com/2019/04/18/waiting-for-postgresql-12-report-progress-of-create-index-operations/) ================================================ FILE: postgres/install-postgres-with-uuid-ossp-using-asdf.md ================================================ # Install Postgres With uuid-ossp Using asdf The `uuid-ossp` extension is part of `postgres-contrib` and is often included with installs of PostgreSQL. By default, when installing PostgreSQL with [`asdf`](https://asdf-vm.com/#/) using the [`asdf-postgres`](https://github.com/smashedtoatoms/asdf-postgres) plugin, the `uuid-ossp` extension is not included. Without it I had trouble running schema migrations against a database that was trying to create the `uuid-ossp` extension: > postgresql uuid-ossp.control file missing in extention folder To include `uuid-ossp` when installing Postgres with `asdf`, you'll need to include _extra config options_. For instance, to install Postgres 9.6.21 with `uuid-ossp` included: ```bash $ POSTGRES_EXTRA_CONFIGURE_OPTIONS="--with-uuid=e2fs" asdf install postgres 9.6.21 ``` There are some resources that recommend using `--with-uuid=ossp`, but that appears to require a prerequisite install of a separate package, so I prefer the `e2fs` option. [source](https://github.com/smashedtoatoms/asdf-postgres/issues/4#issuecomment-350592132) ================================================ FILE: postgres/integers-in-postgres.md ================================================ # Integers In Postgres Postgres has three kinds of integers. Or rather three sizes of integers. There are `smallint` (`int2`), `integer` (`int4`), and `bigint` (`int8`) integers. As you might expect, they are 2 byte, 4 byte, and 8 byte integers respectively. They are also signed integers. All of this has implications for what ranges of integers can be represented by each type. The `smallint` integers have 2 bytes to use, so they can be used to represent integers from -32768 to +32767. The `integer` integers have 4 bytes to use, so they can be used to represent integers from -2147483648 to +2147483647. The `bigint` integers have 8 bytes to use, so they can be used to represent integers from -9223372036854775808 to +9223372036854775807. Though columns can be restricted to use a particular-sized integer, postgres is smart enough to default to `integer` and only use `bigint` as necessary when working with integers on the fly. ```sql > select pg_typeof(55); pg_typeof ----------- integer > select pg_typeof(99999999999999999); pg_typeof ----------- bigint ``` ================================================ FILE: postgres/intervals-of-time-by-week.md ================================================ # Intervals Of Time By Week It is pretty common to use hours or days when creating a Postgres interval. However, intervals can also be created in week-sized chunks ```sql > select '2 weeks'::interval; interval ---------- 14 days (1 row) > select make_interval(0,0,7,0,0,0,0); make_interval --------------- 49 days (1 row) ``` ================================================ FILE: postgres/is-it-null-or-not-null.md ================================================ # Is It Null Or Not Null? In PostgreSQL, the standard way to check if something is `NULL` is like so: ```sql select * as wild_pokemons from pokemons where trainer_id is null; ``` To check if something is not null, you just add `not`: ```sql select * as captured_pokemons from pokemons where trainer_id is not null; ``` PostgreSQL also comes with `ISNULL` and `NOTNULL` which are non-standard ways of doing the same as above: ```sql select * as wild_pokemons from pokemons where trainer_id isnull; ``` ```sql select * as captured_pokemons from pokemons where trainer_id notnull; ``` ================================================ FILE: postgres/label-dollar-quoted-strings-with-a-tag.md ================================================ # Label Dollar-Quoted Strings With A Tag In [Escaping String Literals with Dollar Quoting](escaping-string-literals-with-dollar-quoting.md), I showed how PostgreSQL supports escaped string literals so that you don't have to put backslashes everywhere. This is done by opening and closing the string with `$$`. What if your string literal is going to contain a sequence of two `$` symbols? Or a better hypothetical, what if you want to convey some information about what the string represents? For either of these, the _tagged_ dollar-quoting is a great fit. ```sql > select $JSON${"name": "Sally's Bistro", "price": "$$$"}$JSON$::jsonb; jsonb -------------------------------------------- {"name": "Sally's Bistro", "price": "$$$"} (1 row) > select $JSON${"name": "Sally's Bistro", "price": "$$$"}$JSON$::jsonb->'name' as name; name ------------------ "Sally's Bistro" (1 row) ``` The tagged dollar-quoting allows me to write a string that can be cast to `jsonb` without having to think about which characters need to be escaped. In the second example, I'm able to interact with it like any `jsonb` entity. Here, our tag is `JSON`. It helps convey that the string literal represents JSON. A tag "follows the same rules as an unquoted identifier, except that it cannot contain a dollar sign." The tag goes between the dollar signs and is case-sensitive. [source](https://www.postgresql.org/docs/current/sql-syntax-lexical.html#SQL-SYNTAX-DOLLAR-QUOTING) ================================================ FILE: postgres/limit-execution-time-of-statements.md ================================================ # Limit Execution Time Of Statements You can limit the amount of time that postgres will execute a statement by setting a hard timeout. By default the timeout is 0 (see `show statement_timeout;`) which means statements will be given as much time as they need. If you do want to limit your statements, to say, 1 second, you can set the execution time like so ```sql > set statement_timeout = '1s'; SET > show statement_timeout; statement_timeout ------------------- 1s (1 row) ``` Any queries taking longer than 1 second will be aborted with the following message output ``` ERROR: canceling statement due to statement timeout ``` ================================================ FILE: postgres/list-all-columns-of-a-specific-type.md ================================================ # List All Columns Of A Specific Type We can access information about all the columns in our database through the `information_schema` tables; in particular, the `columns` table. After connecting to a particular database, we can list all columns (across all our tables) of a specific type. We just need to know the schema of the tables we are interested in and the type that we want to track down. My application's tables are under the `public` schema and I want to track down all `timestamp` columns. My query can look something like this ```sql > select table_name, column_name, data_type from information_schema.columns where table_schema = 'public' and data_type = 'timestamp without time zone'; table_name | column_name | data_type -----------------+-------------+----------------------------- articles | created_at | timestamp without time zone articles | updated_at | timestamp without time zone users | created_at | timestamp without time zone users | updated_at | timestamp without time zone (4 rows) ``` Alternatively, I could look for both `timestamp` and `timestamptz` with a query like this ```sql > select table_name, column_name, data_type from information_schema.columns where table_schema = 'public' and data_type like '%timestamp%'; ``` ================================================ FILE: postgres/list-all-rows-in-a-table.md ================================================ # List All Rows In A Table Perhaps the more common way to list all rows in a table is with the following `select` command: ```sql select * from bedding_types; ``` There is an alternative approach that also selects all rows from a table. It's essentially a shorthand -- the `table` command. ```sql > table bedding_types; name ---------- No Bed 1 Full 1 Double 2 Double 1 Twin 2 Twins 1 Queen 2 Queen 1 King 2 Kings 3 Kings Murphy Sofa Bed ``` h/t Jack Christensen ================================================ FILE: postgres/list-all-the-databases.md ================================================ # List All The Databases There are two ways to list all the available databases. The first is a `psql` only command: ``` \list ``` The second approach is to query the `pg_database` table. Something like the following will suffice: ```sql select datname from pg_database; ``` ================================================ FILE: postgres/list-all-versions-of-a-function.md ================================================ # List All Versions Of A Function Within `psql` you can use `\df` to list a postgres function with a given name ```sql > \df now List of functions Schema | Name | Result data type | Argument data types | Type ------------+------+--------------------------+---------------------+-------- pg_catalog | now | timestamp with time zone | | normal (1 row) ``` When a function has multiple definitions across a number of types, `\df` will list all versions of that function ```sql > \df generate_series List of functions -[ RECORD 1 ]-------+------------------------------------------------------------------- Schema | pg_catalog Name | generate_series Result data type | SETOF bigint Argument data types | bigint, bigint Type | normal -[ RECORD 2 ]-------+------------------------------------------------------------------- Schema | pg_catalog Name | generate_series Result data type | SETOF bigint Argument data types | bigint, bigint, bigint Type | normal -[ RECORD 3 ]-------+------------------------------------------------------------------- Schema | pg_catalog Name | generate_series Result data type | SETOF integer Argument data types | integer, integer Type | normal -[ RECORD 4 ]-------+------------------------------------------------------------------- Schema | pg_catalog Name | generate_series Result data type | SETOF integer Argument data types | integer, integer, integer Type | normal -[ RECORD 5 ]-------+------------------------------------------------------------------- Schema | pg_catalog Name | generate_series Result data type | SETOF timestamp with time zone Argument data types | timestamp with time zone, timestamp with time zone, interval Type | normal -[ RECORD 6 ]-------+------------------------------------------------------------------- Schema | pg_catalog Name | generate_series Result data type | SETOF timestamp without time zone Argument data types | timestamp without time zone, timestamp without time zone, interval Type | normal ``` ================================================ FILE: postgres/list-available-schemas.md ================================================ # List Available Schemas Use the `\dn` command within a `psql` session to list the available schemas. This will only included user created schemas. This means that schemas like `public` will be listed whereas schemas like `information_schema` and `pg_catalog` will not. You can use `\dnS` to also list system schemas. [source](http://www.postgresql.org/docs/current/static/app-psql.html) ================================================ FILE: postgres/list-connections-to-a-database.md ================================================ # List Connections To A Database The `pg_stat_activity` table can be used to determine what connections there currently are to the PostgreSQL server and to a particular database. To see the process ids and usernames of all connection to your PostgreSQL server, run the following query: ```sql > select pid, usename from pg_stat_activity; pid | usename -------+------------ 57174 | jbranchaud 83420 | jbranchaud ``` Include `datname` in the requested columns to figure out the database of each connection. ```sql > select pid, usename, datname from pg_stat_activity; pid | usename | datname -------+------------+----------- 57174 | jbranchaud | hr_hotels 83420 | jbranchaud | pgbyex ``` The results can be restricted to a particular database as necessary. ```sql > select pid, usename from pg_stat_activity where datname = 'hr_hotels'; pid | usename -------+------------ 57174 | jbranchaud ``` ================================================ FILE: postgres/list-database-objects-with-disk-usage.md ================================================ # List Database Objects With Disk Usage I'll often times use `\d` or `\dt` to check out the tables in my database. This shows the schema, object name, object type (e.g. `table`), and owner for each. By adding the `+` to that meta-command, I can also see the disk usage for each database object. Here is an example of look at all tables in a database with the additional `Size` (or disk usage) information: ```sql > \dt+ List of relations Schema | Name | Type | Owner | Size | Description --------+--------------------+-------+------------+------------+------------- public | amount_types | table | jbranchaud | 16 kB | public | ingredient_amounts | table | jbranchaud | 8192 bytes | public | ingredient_types | table | jbranchaud | 16 kB | public | ingredients | table | jbranchaud | 48 kB | public | recipes | table | jbranchaud | 16 kB | public | schema_migrations | table | jbranchaud | 16 kB | public | users | table | jbranchaud | 16 kB | ``` ================================================ FILE: postgres/list-database-users.md ================================================ # List Database Users Within `psql`, type `\du` to list all the users for a database and their respective permissions. ```bash > \du List of roles Role name | Attributes | Member of ------------+------------------------------------------------+----------- jbranchaud | Superuser, Create role, Create DB, Replication | {} sampleuser | Create DB | {} ``` ================================================ FILE: postgres/list-databases-available-for-connecting.md ================================================ # List Databases Available For Connecting I tend to have a couple different versions of Postgres installed on my development machine. Each server version tends to have a different set of databases. As I switch between projects and Postgres versions, it can be hard to remember the name of the database to which I want to connect when using `psql`. I usually connect to one of the defaults, which is either named `postgres` or named after the machine user. There is a better way. I can first ask `psql` to list all the available databases. ``` ❯ psql --list Timing is on. List of databases Name | Owner | Encoding | Collate | Ctype | Access privileges --------------------------------+------------+----------+-------------+-------------+--------------------------- jbranchaud | jbranchaud | UTF8 | en_US.UTF-8 | en_US.UTF-8 | postgres | jbranchaud | UTF8 | en_US.UTF-8 | en_US.UTF-8 | thirty_days_server_development | jbranchaud | UTF8 | en_US.UTF-8 | en_US.UTF-8 | thirty_days_server_test | jbranchaud | UTF8 | en_US.UTF-8 | en_US.UTF-8 | (4 rows) ``` Then I know before connecting which one I'm looking for or if it must be in the data directory of another Postgres server version. ================================================ FILE: postgres/list-various-kinds-of-objects.md ================================================ # List Various Kinds Of Objects Our PostgreSQL database can end up with all kinds of objects: tables, sequences, views, etc. We can use a variety of `psql` meta-commands to list the different types of (user-created) objects in our database. - `\dt` will list all the tables - `\dE` will list all the foreign tables - `\di` will list all the indexes - `\ds` will list all the sequences - `\dv` will list all the views - `\dm` will list all the materialized views These can also be combined. For instance, to see all the tables and sequences, we can run `\dts`. ================================================ FILE: postgres/lower-is-faster-than-ilike.md ================================================ # Lower Is Faster Than ilike There are a couple ways to do a case-insensitive comparison of data in PostgreSQL. One way is to use the `ilike` operator for comparison. Another way is to use the `lower()` function on both sides of the `=` operator for comparison. Using `lower()` is a bit faster than using `ilike`. When comparing ```sql select * from users where email ilike 'some-email@example.com'; ``` to ```sql select * from users where lower(email) = lower('some-email@example.com'); ``` we find (via `explain analyze`) that using `lower()` was taking around 12ms where as the `ilike` example was taking around 17ms. We earn orders of magnitude in performance when adding a functional index that uses the `lower()` function like so: ```sql create unique index users_unique_lower_email_idx on users (lower(email)); ``` After adding this index, the example using `lower()` drops to around 0.08ms. For the full example and `explain analyze` outputs, [see this document](https://github.com/jbranchaud/postgresing/blob/master/ilike_vs_lower.sql). ================================================ FILE: postgres/manage-major-versions-with-brew-and-direnv.md ================================================ # Manage Major Versions With Brew and Direnv I can install multiple major versions of PostgreSQL to my machine with `brew` with commands like the following: ```bash $ brew install postgresql@14 $ brew install postgresql@16 $ # ... ``` I can then start and stop specific Postgres server versions using the `brew services start/stop` commands. Let's say 14 is running, but I want to switch to 16. ```bash $ brew services stop postgresql@14 $ brew services start postgresql@16 ``` Then I can use [`direnv`](https://direnv.net/) to manage the Postgres client (bin directory) that gets used for certain projects. This way when I run a command like `pg_dump` or `psql` it will use the one associated with a specific major version of Postgres. Assuming I already have `direnv` installed, I can add a `.envrc` to my project with the following line: ``` PATH_add /usr/local/opt/postgresql@16/bin ``` And then tell my system that the changes to this file are allowed to be sourced by `direnv`: ```bash $ direnv allow . ``` That's it. Now I have PostgreSQL 16 (client and server) ready to use for my current project. ================================================ FILE: postgres/max-identifier-length-is-63-bytes.md ================================================ # Max Identifier Length Is 63 Bytes In PostgreSQL, identifiers -- table names, column names, constraint names, etc. -- are limited to a maximum length of 63 bytes. Identifiers longer than 63 characters can be used, but they will be truncated to the allowed length of 63. ```sql > alter table articles add constraint this_constraint_is_going_to_be_longer_than_sixty_three_characters_id_idx check (char_length(title) > 0); NOTICE: identifier "this_constraint_is_going_to_be_longer_than_sixty_three_characters_id_idx" will be truncated to "this_constraint_is_going_to_be_longer_than_sixty_three_characte" ALTER TABLE ``` Postgres warns us of identifiers longer than 63 characters, informing us of what they will be truncated to. It then proceeds to create the identifier. If postgres is trying to generate an identifier for us - say, for a foreign key constraint - and that identifier is longer than 63 characters, postgres will truncate the identifier somewhere in the middle so as to maintain the convention of terminating with, for example, `_fkey`. The 63 byte limit is not arbitrary. It comes from `NAMEDATALEN - 1`. By default `NAMEDATALEN` is 64. If need be, this value can be modified in the Postgres source. Yay, open-source database implementations. See [the postgres docs](http://www.postgresql.org/docs/current/static/sql-syntax-lexical.html#SQL-SYNTAX-IDENTIFIERS) for more details. ================================================ FILE: postgres/open-heroku-database-in-postico-from-terminal.md ================================================ # Open Heroku Database In Postico From Terminal I recently downloaded [Postico](https://eggerapps.at/postico/) at the recommendation of [Dillon Hafer](https://dillonhafer.com/). I tend to use `psql` as a PostgreSQL client for all my database querying needs. However, Dillon highly recommended Postico for doing system admin querying. I needed to connect directly to a production Postgres server on Heroku to investigate slow queries. Postico presented me with a form of individual fields for host, port, username, password, database, etc. This would have been a little annoying to fill in manually. Dillon had a shortcut to recommend. From the command line you can open Postico with a connection string. It knows how to split that connection string into the respective fields. ```bash heroku config:get DATABASE_URL --app APP_NAME | xargs open -a Postico ``` This requests the `DATABASE_URL` from Heroku. It is a Postgres connection string with all the fields needed to connect to a remove server. This is then passed via `xargs` to `Postico` as it is being `open`ed. ================================================ FILE: postgres/output-explain-query-plan-in-different-formats.md ================================================ # Output Explain Query Plan In Different Formats The output of an [`explain` (or `explain analyze`) query plan](https://www.postgresql.org/docs/current/sql-explain.html) for a given query defaults to a `TEXT` format that is meant to be read by a person. ```sql > explain (analyze) select title from books where created_at > now() - '1 year'::interval; QUERY PLAN ------------------------------------------------------------------------------------------------- Seq Scan on books (cost=0.00..1.28 rows=5 width=32) (actual time=0.011..0.017 rows=22 loops=1) Filter: (created_at > (now() - '1 year'::interval)) Planning Time: 0.052 ms Execution Time: 0.027 ms (4 rows) ``` If we instead want the query plan in a standardized format that is parseable and readable by a program, we can specify an alternate format like `JSON`, `YAML`, or `XML`. Here is the same plan with `format json`: ```sql > explain (analyze, format json) select title from books where created_at > now() - '1 year'::interval; QUERY PLAN ---------------------------------------------------------------- [ + { + "Plan": { + "Node Type": "Seq Scan", + "Parallel Aware": false, + "Async Capable": false, + "Relation Name": "books", + "Alias": "books", + "Startup Cost": 0.00, + "Total Cost": 1.28, + "Plan Rows": 5, + "Plan Width": 32, + "Actual Startup Time": 0.008, + "Actual Total Time": 0.014, + "Actual Rows": 22, + "Actual Loops": 1, + "Filter": "(created_at > (now() - '1 year'::interval))",+ "Rows Removed by Filter": 0 + }, + "Planning Time": 0.050, + "Triggers": [ + ], + "Execution Time": 0.023 + } + ] (1 row) ``` I present all four formats for a complex query plan [in this Gist](https://gist.github.com/jbranchaud/731b1a68f5cc70c4f7a9e1f5ef570836). ================================================ FILE: postgres/pg-prefix-is-reserved-for-system-schemas.md ================================================ # pg Prefix Is Reserved For System Schemas Have you ever tried to create a schema with `pg_` as the first part of the name of the schema? If so, you probably didn't get very far. Postgres won't let you do that. It reserves the `pg_` prefix for system schemas. If you try to create a schema in this way, you'll get an *unacceptable schema name* error. ```sql > create schema pg_cannot_do_this; ERROR: unacceptable schema name "pg_cannot_do_this" DETAIL: The prefix "pg_" is reserved for system schemas. ``` ================================================ FILE: postgres/postgres-does-not-support-unsigned-integers.md ================================================ # Postgres Does Not Support Unsigned Integers PostgreSQL has a variety of sizes of integer types, from `smallint` (2 bytes) to `integer` (4 bytes) to `bigint` (8 bytes), as well as [other numeric types](https://www.postgresql.org/docs/current/datatype-numeric.html). It does _not_ however support unsigned versions of these numeric types. That means, with an `integer` for instance, we can store numbers between `-2147483648` and `+2147483647`. That's everything that can fit into 4 bytes. In a system that supported 4 byte unsigned integers we'd be able to represent from `0` all the way up to `4294967295`. In PostgreSQL, we're limited to these _signed_ numeric types. That means if we were hoping that the data type could essentially enforce a non-negative restriction on the data in one of our columns, we're going to have to be more creative. The obvious choice to me is to consider adding a [check constraint](https://www.postgresql.org/docs/current/ddl-constraints.html#DDL-CONSTRAINTS-CHECK-CONSTRAINTS) (e.g. `quantity integer check (quantity > 0)`). Another option, as pointed out by [this StackOverflow answer](https://stackoverflow.com/a/31833279/535590), is to create [a user-defined _domain type_](https://www.postgresql.org/docs/current/domains.html) that restricts valid values. To me, the ergonomics of using a domain type are a bit awkward and not worth the effort. With either of these solutions, we are only approximating an unsigned integer and do not actually have the same range of values available. ================================================ FILE: postgres/prepare-execute-and-deallocate-statements.md ================================================ # Prepare, Execute, and Deallocate Statements In PostgreSQL, you can prepare a named statement to be executed later using [`prepare`](https://www.postgresql.org/docs/current/static/sql-prepare.html). ```sql > prepare column_names (text) as select column_name from information_schema.columns where table_name = $1; PREPARE ``` These statements are kept around for the duration of the session. To see the available statements, check out the `pg_prepared_statements` view. ```sql > select * from pg_prepared_statements; name | statement | prepare_time | parameter_types | from_sql --------------+-----------------------------------------------------------------------------+-------------------------------+-----------------+---------- column_names | prepare column_names (text) as +| 2017-03-10 15:01:09.154528-06 | {text} | t | select column_name from information_schema.columns where table_name = $1; | | | ``` To run a prepared statement, use `execute` with the name of the statement and any arguments. ```sql > execute column_names('users'); column_name ----------------- id email password_digest created_at updated_at first_name last_name ``` You can also delete a statement with [`deallocate`](https://www.postgresql.org/docs/current/static/sql-deallocate.html) if you'd like. ```sql > deallocate column_names; DEALLOCATE ``` ================================================ FILE: postgres/pretty-print-data-sizes.md ================================================ # Pretty Print Data Sizes Use the `pg_size_pretty()` function to pretty print the sizes of data in PostgreSQL. Given a `bigint`, it will determine the most human-readable format with which to print the value: ```sql > select pg_size_pretty(1234::bigint); pg_size_pretty ---------------- 1234 bytes > select pg_size_pretty(123456::bigint); pg_size_pretty ---------------- 121 kB > select pg_size_pretty(1234567899::bigint); pg_size_pretty ---------------- 1177 MB > select pg_size_pretty(12345678999::bigint); pg_size_pretty ---------------- 11 GB ``` This function is particularly useful when used with the [`pg_database_size()`](get-the-size-of-a-database.md) and [`pg_relation_size()`](get-the-size-of-a-table.md) functions. ```sql > select pg_size_pretty(pg_database_size('hr_hotels')); pg_size_pretty ---------------- 12 MB ``` ================================================ FILE: postgres/pretty-printing-jsonb-rows.md ================================================ # Pretty Printing JSONB Rows Who needs a document store when you can just use PostgreSQL's `JSONB` data type? Viewing rows of `JSONB` output can be challenging though because it defaults to printing them as a single line of text. ```sql > select '{"what": "is this", "nested": {"items 1": "are the best", "items 2": [1, 2, 3]}}'::jsonb; jsonb ---------------------------------------------------------------------------------- {"what": "is this", "nested": {"items 1": "are the best", "items 2": [1, 2, 3]}} (1 row) ``` Fortunately, Postgres comes with a function for prettying up the format of the output of these rows -- [`jsonb_pretty`](https://www.postgresql.org/docs/current/static/functions-json.html) ```sql > select jsonb_pretty('{"what": "is this", "nested": {"items 1": "are the best", "items 2": [1, 2, 3]}}'::jsonb); jsonb_pretty ------------------------------------ { + "what": "is this", + "nested": { + "items 1": "are the best",+ "items 2": [ + 1, + 2, + 3 + ] + } + } (1 row) ``` h/t Jack Christensen ================================================ FILE: postgres/prevent-a-query-from-running-too-long.md ================================================ # Prevent A Query From Running Too Long A number of different factors can effect how long a query takes to run. Certainly the size of a table and the complexity of the query play a big role. Locking can really slow a query down by making it wait to get started. A series of competing queries that induce table locking can grind things to a halt. If you don't want queries in a particular connection being allowed to wait or run too long, you can set a timeout. ```sql set statement_timeout to '500'; ``` That will ensure that any statement run in that connection will be terminated if it takes longer than 500ms. You can also specify a unit: ```sql set statement_timeout to '15s'; ``` That will enforce statement timeout of 15 seconds. See the [docs](https://www.postgresql.org/docs/current/runtime-config-client.html) for more details. ================================================ FILE: postgres/print-the-query-buffer-in-psql.md ================================================ # Print The Query Buffer In psql I'll often be composing a PostgreSQL query in Vim and decide I want to give it a try in `psql`. I copy the relevant snippet of SQL to my system buffer and then paste into `psql`. I'm usually hit with a mess of text like this though: ```sql jbranchaud=# create table nullable_fields ( jbranchaud(# id serial primary key, first varchar, last varchar ) id serial primary key, jbranchaud(# first varchar, last varchar ) first varchar, jbranchaud(# last varchar ) last varchar jbranchaud(# ) ) jbranchaud-# ``` Yikes. That's not readable. Fortunately, `psql` provides a command for printing the current contents of the query buffer. By typing `\p` I'll see a more readable version of what I just pasted in. ```sql jbranchaud-# \p create table nullable_fields ( id serial primary key, first varchar, last varchar ) jbranchaud-# ``` After taking another glance at the snippet of SQL, I decide to complete the query to create my new table. ```sql jbranchaud-# ; CREATE TABLE ``` ================================================ FILE: postgres/put-unique-constraint-on-generated-column.md ================================================ # Put Unique Constraint On Generated Column You cannot apply a _unique constraint_ to an expression over a column, e.g. `lower(email)`. You can, however, create a [generated column](https://www.postgresql.org/docs/current/ddl-generated-columns.html) for that expression and then apply the unique constraint to that generated column. Here is what that could look like: ```sql > create table users ( id integer generated always as identity primary key, name text not null, email text not null, email_lower text generated always as (lower(email)) stored, unique ( email_lower ) ); > \d users +-------------+---------+-----------------------------------------------------------------+ | Column | Type | Modifiers | |-------------+---------+-----------------------------------------------------------------| | id | integer | not null generated always as identity | | name | text | not null | | email | text | not null | | email_lower | text | default lower(email) generated always as (lower(email)) stored | +-------------+---------+-----------------------------------------------------------------+ Indexes: "users_pkey" PRIMARY KEY, btree (id) "users_email_lower_key" UNIQUE CONSTRAINT, btree (email_lower) ``` And then an demonstration of violating that constraint: ```sql > insert into users (name, email) values ('Bob', 'bob@email.com'); INSERT 0 1 > insert into users (name, email) values ('Bobby', 'BOB@email.com'); duplicate key value violates unique constraint "users_email_lower_key" DETAIL: Key (email_lower)=(bob@email.com) already exists. ``` The main tradeoff here is that you are doubling the amount of storage you need for that column. Unless it is a massive table, that is likely not an issue. ================================================ FILE: postgres/references-target-primary-key-by-default.md ================================================ # References Target Primary Key By Default Typically when I am creating a table or adding a column that involves a foreign key constraint, I explicitly name the reference column. ```sql create table contacts ( id int generated always as identity primary key, user_id int references users(id); ); ``` The [Create Table PostgreSQL Docs](https://www.postgresql.org/docs/17/sql-createtable.html) point out that specifying the reference column isn't strictly necessary. > These clauses specify a foreign key constraint, which requires that a group > of one or more columns of the new table must only contain values that match > values in the referenced column(s) of some row of the referenced table. If > the refcolumn list is omitted, the primary key of the reftable is used. If we're using the primary key as the reference column, then we can choose to omit the reference column. ```sql create table contacts ( id int generated always as identity primary key, user_id int references users; ); ``` In the same way we can do this when adding a column. ```sql alter table contacts add column account_id int references account; ``` ================================================ FILE: postgres/remove-not-null-constraint-from-a-column.md ================================================ # Remove Not Null Constraint From A Column When you want to add a [`not null`](https://www.postgresql.org/docs/current/ddl-constraints.html#id-1.5.4.6.6) constraint to a column, you do so by _setting_ it. ```sql alter table books alter column publication_date set not null; ``` You can remove a `not null` constraint from a column, by _dropping_ it. ```sql alter table books alter column publication_date drop not null; ``` Notice this excerpt of syntax from the official Postgres docs: ``` ... ALTER [ COLUMN ] column_name { SET | DROP } NOT NULL ``` [source](https://www.postgresql.org/docs/current/sql-altertable.html) ================================================ FILE: postgres/renaming-a-sequence.md ================================================ # Renaming A Sequence If a table is created with a `serial` type column, then a sequence is also created with a name based on the name of the table. ```sql > \d List of relations Schema | Name | Type | Owner --------+-----------------+----------+------------ public | accounts | table | jbranchaud public | accounts_id_seq | sequence | jbranchaud ``` In [Renaming A Table](renaming-a-table.md), I showed how a table can be renamed in PostgreSQL. This will not, however, rename associated sequences. To maintain naming consistency, you may want to also rename sequences when renaming tables. This can be done with a query like the following: ```sql > alter sequence accounts_id_seq rename to users_id_seq; ``` See the [`alter sequence`](http://www.postgresql.org/docs/current/static/sql-altersequence.html) docs for more details. ================================================ FILE: postgres/renaming-a-table.md ================================================ # Renaming A Table Using the `alter table` command in PostgreSQL, you can rename an existing table. This command will also update any references to the table such as via foreign key constraints. Just run a command like the following: ```sql alter table ingredient_types rename to item_types; ``` Note that this may result in breaking a number of conventions. Foreign keys, sequences, and constraints with names eponymous to the original table will no longer follow convention despite the references being updated. These can be renamed as well if desired. See [`renaming_table.sql`](https://github.com/jbranchaud/postgresing/blob/master/renaming/rename_table.sql) for a full example. See the [`alter table` docs](http://www.postgresql.org/docs/current/static/sql-altertable.html) for more details. ================================================ FILE: postgres/restart-a-sequence.md ================================================ # Restart A Sequence In postgres, if you are truncating a table or doing some other sort of destructive action on a table in a development or testing environment, you may notice that the id sequence for the primary key just keeps plugging along from where it last left off. The sequence can be reset to any value like so: ```sql > alter sequence my_table_id_seq restart with 1; ALTER SEQUENCE ``` [source](http://www.postgresql.org/docs/current/static/sql-altersequence.html) ================================================ FILE: postgres/restarting-sequences-when-truncating-tables.md ================================================ # Restarting Sequences When Truncating Tables PostgreSQL's [`truncate`](http://www.postgresql.org/docs/current/static/sql-truncate.html) feature is a handy way to clear out all the data from a table. If you use `truncate` on a table that has a `serial` primary key, you may notice that subsequent insertions keep counting up from where you left off. This is because the sequence the table is using hasn't been restarted. Sure, you can restart it manually or you can tell `truncate` to do it for you. By appending `restart identity` to the end of a `truncate` statement, Postgres will make sure to restart any associated sequences at `1`. ```sql truncate pokemons, trainers, pokemons_trainers restart identity; ``` ================================================ FILE: postgres/salt-and-hash-a-password-with-pgcrypto.md ================================================ # Salt And Hash A Password With pgcrypto The [`pgcrypto`](http://www.postgresql.org/docs/current/static/pgcrypto.html) extension that ships with PostgreSQL can be used to do a number of interesting things. This includes functions for doing salted password hashing. Using the `crypt` and `gen_salt` functions, we can securely store a user password and later compare it to plain-text passwords for authentication purposes. ```sql create extension pgcrypto; select crypt('pa$$w0rd', gen_salt('bf')); crypt -------------------------------------------------------------- $2a$06$Z7wmrkYMOyLboLcULUYzNe6nHUcWywSZTt6nSrT5Xdv/VLdJ4g99K > select ( '$2a$06$Z7wmrkYMOyLboLcULUYzNe6nHUcWywSZTt6nSrT5Xdv/VLdJ4g99K' = crypt( 'pa$$w0rd', '$2a$06$Z7wmrkYMOyLboLcULUYzNe6nHUcWywSZTt6nSrT5Xdv/VLdJ4g99K' ) ) as matched; matched --------- t > select ( '$2a$06$Z7wmrkYMOyLboLcULUYzNe6nHUcWywSZTt6nSrT5Xdv/VLdJ4g99K' = crypt( 'password', '$2a$06$Z7wmrkYMOyLboLcULUYzNe6nHUcWywSZTt6nSrT5Xdv/VLdJ4g99K' ) ) as matched; matched --------- f ``` The salt value is generated using the blowfish encryption algorithm (hence, the `'bf'`). There is support for other algorithms such as `md5`. See the [`pgcrypt` documentation](http://www.postgresql.org/docs/current/static/pgcrypto.html) for more details. ================================================ FILE: postgres/send-a-command-to-psql.md ================================================ # Send A Command To psql You can send a command to `psql` to be executed by using the `-c` flag ```bash $ psql -c "select 'Hello, World!';" ?column? --------------- Hello, World! (1 row) ``` Specify a particular database as needed ```bash $ psql blog_prod -c 'select count(*) from posts;' count ------- 8 (1 row) ``` h/t Jack Christensen ================================================ FILE: postgres/set-a-seed-for-the-random-number-generator.md ================================================ # Set A Seed For The Random Number Generator In PostgreSQL, the internal seed for the random number generator is a run-time configuration parameter. This `seed` parameter can be set to a particular seed in order to get some determinism from functions that utilize the random number generator. The seed needs to be something between `0` and `1`. We can see this in action by setting the seed and then invoking `random()` a couple times. Doing this twice, we will see the reproducibility we can achieve with a seed. ```sql > set seed to 0.1234; SET > select random(); random ------------------- 0.397731185890734 > select random(); random ------------------ 0.39575699577108 (1 row) > set seed to 0.1234; SET > select random(); random ------------------- 0.397731185890734 > select random(); random ------------------ 0.39575699577108 ``` The seed can also be configured with the `setseed()` function. See [the PostgreSQL docs](http://www.postgresql.org/docs/8.3/static/sql-set.html) for more details. ================================================ FILE: postgres/set-a-statement-timeout-threshold-for-a-session.md ================================================ # Set A Statement Timeout Threshold For A Session The `statement_timeout` variable is used to tell the PostgreSQL server that you want it to terminate statements (queries and transactions) that run past the specified threshold. This is a great way to [prevent runaway queries](https://blog.crunchydata.com/blog/control-runaway-postgres-queries-with-statement-timeout) in a production environment. You can set this threshold with a `set` statement. It can take an integer argument of milliseconds. Here I set it to a timeout of 1 minute. ```sql > set statement_timeout = 60000; SET > show statement_timeout; statement_timeout ------------------- 1min (1 row) ``` This will set the `statement_timeout` for the duration of the session. It won't effect other sessions. You can also set the threshold with a string argument which allows you to include a unit of time. Here I set it to 30 seconds. ```sql > set statement_timeout = '30s'; SET > show statement_timeout; statement_timeout ------------------- 30s (1 row) ``` Now that the `statement_timeout` is set to `30s`, I can run a query that I know will exceed that threshold ([`pg_sleep`](https://www.postgresql.org/docs/current/functions-datetime.html#FUNCTIONS-DATETIME-DELAY)). ```sql > select pg_sleep(31); ERROR: canceling statement due to statement timeout Time: 30001.997 ms (00:30.002) ``` After 30 seconds have passed, the Postgres server will interrupt the query. ================================================ FILE: postgres/set-inclusion-with-hstore.md ================================================ # Set Inclusion With hstore In PostgreSQL, `hstore` records can be compared via set inclusion. The `@>` and `<@` operators can be used for this. The `@>` operator checks if the right operand is a subset of the left operand. The `<@` operator checks if the left operand is a subset of the right operand. ```sql > select '"one"=>"1", "two"=>"2", "three"=>"3"'::hstore @> '"two"=>"2"'::hstore; ?column? ---------- t > select '"one"=>"1", "two"=>"2", "three"=>"3"'::hstore <@ '"two"=>"2"'::hstore; ?column? ---------- f ``` See the [`hstore` PostgreSQL docs](http://www.postgresql.org/docs/current/static/hstore.html) for more details. ================================================ FILE: postgres/set-up-a-project-local-cluster-with-postgres-app.md ================================================ # Set Up A Project-Local Cluster With Postgres.app I want to set up a PostgreSQL cluster in my project directory. This helps provide some separation and clarity that this cluster and its databases are just for this project. This can be done with `Postgres.app` (on Mac) hitting the `+` button in the bottom left corner of the app. This will pop open a "Create new server" modal. From there, you'll want to give the server a name that you can identify within `Postgres.app`. E.g. " Cluster" Then select the Postgres version. My existing project is still on 17, so I'll select that. The not so intuitive part is the _Data Directory_. Use the "Choose..." file picker to find the root directory of your project. Select that. Then click into the text input for the data directory and append the name of the data directory _to be created_ to that path. If I want it to all go in `postgres-data`, then my path will look like: ``` /Users/me/dev/my-app/postgres-data ``` The `postgres-data` directory doesn't exist yet. But it will in a moment. You probably want the default port, so leave that at `5432` unless you know otherwise. Click `Create server`, though that won't actually create the server yet. Now with that server selected in `Postgres.app` click the `Initialize` button. That will create the `postgres-data` directory and then run `initdb` under the hood which will add everything your server needs. It will now be running at that port, ready to connect. ================================================ FILE: postgres/sets-with-the-values-command.md ================================================ # Sets With The Values Command You can concisely create sets of values in PostgreSQL using the `values` command. ```sql > values (1), (2), (3); column1 --------- 1 2 3 ``` You can even create multiple columns of values. ```sql > values (1, 'a', true), (2, 'b', false); column1 | column2 | column3 ---------+---------+--------- 1 | a | t 2 | b | f ``` This is most often used with an insert command, but can be used on its own, as a subquery, within a CTE, etc. [source](http://www.postgresql.org/docs/current/static/sql-values.html) ================================================ FILE: postgres/shorthand-absolute-value-operator.md ================================================ # Shorthand Absolute Value Operator Postgres offers many [math functions](https://www.postgresql.org/docs/8.0/functions-math.html) including `abs` for computing the absolute value of a number. ```sql > select abs(-1); abs ----- 1 (1 row) ``` There is also an absolute value _operator_ -- the `@` symbol. This can be used to do the same thing. ```sql > select @ -1; ?column? ---------- 1 (1 row) ``` [source](https://kb.objectrocket.com/postgresql/why-use-postgres-abs-function-in-sql-729) ================================================ FILE: postgres/show-all-versions-of-an-operator.md ================================================ # Show All Versions Of An Operator We may be familiar with PostgreSQL's containment operator (`@>`). Maybe we've used it with an array before, so we understand the general idea. But now we are curious about what are the other types with which this containment operator can be used. We can quickly find out the answer with the `\do` command in `psql`: ```sql > \do @> List of operators Schema | Name | Left arg type | Right arg type | Result type | Description ------------+------+---------------+----------------+-------------+------------- pg_catalog | @> | aclitem[] | aclitem | boolean | contains pg_catalog | @> | anyarray | anyarray | boolean | contains pg_catalog | @> | anyrange | anyelement | boolean | contains pg_catalog | @> | anyrange | anyrange | boolean | contains pg_catalog | @> | box | box | boolean | contains pg_catalog | @> | box | point | boolean | contains pg_catalog | @> | circle | circle | boolean | contains pg_catalog | @> | circle | point | boolean | contains pg_catalog | @> | jsonb | jsonb | boolean | contains pg_catalog | @> | path | point | boolean | contains pg_catalog | @> | polygon | point | boolean | contains pg_catalog | @> | polygon | polygon | boolean | contains pg_catalog | @> | tsquery | tsquery | boolean | contains ``` The `Left arg type` and `Right arg type` columns tell us what we need to know. This `\do` command can be used with any operator for a similar set of information. h/t Bruce Momjian ================================================ FILE: postgres/show-reconstructed-constraints-for-a-table.md ================================================ # Show Reconstructed Constraints For A Table The [`pg_get_constraintdef` function](https://pgpedia.info/p/pg_get_constraintdef.html) can be used to reconstruct the command for creating a given constraint. This isn't necessarily the command (or commands) that originally created the constraint, but rather a reconstruction. We have to pass it an `oid` that corresponds to the constraint which we can get from the `pg_constraint` table. These results can be further narrowed down by the `conname` (constraint name) and `conrelid` (table name). Here is an example of listing the constraints on a `reading_statuses` table. ```sql > select conname, pg_get_constraintdef(oid) from pg_constraint where conrelid = 'reading_statuses'::regclass; conname | pg_get_constraintdef -------------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- reading_statuses_pkey | PRIMARY KEY (id) fk_rails_17ee7cb2c4 | FOREIGN KEY (user_id) REFERENCES users(id) fk_rails_0d3729339f | FOREIGN KEY (book_id) REFERENCES books(id) reading_statuses_valid_status_check | CHECK (((status)::text = ANY ((ARRAY['started'::character varying, 'completed'::character varying, 'abandoned'::character varying, 'already_read'::character varying])::text[]))) (4 rows) ``` I came across this while experimenting with [an idea for a fail-fast Rails initializer check](https://gist.github.com/jbranchaud/12813a0558f9cd06bcc24b7d8706550c) that verifies the values of the `reading_statuses_valid_status_check` stay in sync with the Rails version of those values that live in a constant. ================================================ FILE: postgres/show-the-hidden-queries-behind-backslash-commands.md ================================================ # Show The Hidden Queries Behind Backslash Commands The `ECHO_HIDDEN` variable in PostgreSQL's `psql` determines whether the queries behind backslash commands are displayed. It defaults to `false`. So, generally, when you run something like `\d` or `\l+`, you'll just see the result and not the query that helped produce it. If you're curious what's behind any of these backslash commands, then set `ECHO_HIDDEN` to `true` to get a look. ```sql > \set ECHO_HIDDEN true > \d ********* QUERY ********** SELECT n.nspname as "Schema", c.relname as "Name", CASE c.relkind WHEN 'r' THEN 'table' WHEN 'v' THEN 'view' WHEN 'm' THEN 'materialized view' WHEN 'i' THEN 'index' WHEN 'S' THEN 'sequence' WHEN 's' THEN 'special' WHEN 'f' THEN 'foreign table' WHEN 'p' THEN 'partitioned table' WHEN 'I' THEN 'partitioned index' END as "Type", pg_catalog.pg_get_userbyid(c.relowner) as "Owner" FROM pg_catalog.pg_class c LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace WHERE c.relkind IN ('r','p','v','m','S','f','') AND n.nspname <> 'pg_catalog' AND n.nspname <> 'information_schema' AND n.nspname !~ '^pg_toast' AND pg_catalog.pg_table_is_visible(c.oid) ORDER BY 1,2; ************************** List of relations Schema | Name | Type | Owner --------+--------------+----------+------------ public | users | table | jbranchaud public | users_id_seq | sequence | jbranchaud ``` That query is what `psql` uses to list the relations for your current database. ================================================ FILE: postgres/sleeping.md ================================================ # Sleeping Generally you want your SQL statements to run against your database as quickly as possible. For those times when you are doing some sort of debugging or just want your queries to look very computationally expensive, PostgreSQL offers the `pg_sleep` function. To sleep for 5 seconds, try the following: ```sql > select now(); select pg_sleep(5); select now(); now ------------------------------- 2016-01-08 16:30:21.251081-06 (1 row) Time: 0.274 ms pg_sleep ---------- (1 row) Time: 5001.459 ms now ------------------------------- 2016-01-08 16:30:26.252953-06 (1 row) Time: 0.260 ms ``` As you'll notice, the `pg_sleep` statement took about 5 seconds. [source](http://www.if-not-true-then-false.com/2010/postgresql-sleep-function-pg_sleep-postgres-delay-execution/) ================================================ FILE: postgres/special-math-operators.md ================================================ # Special Math Operators Postgres has all the mathematical operators you might expect in any programming language (e.g. `+`,`-`,`*`,`/`,`%`). It also has a few extras that you might not be expecting. Factorial Operator: ```sql > select 5!; ?column? ---------- 120 (1 row) ``` Square Root Operator: ```sql > select |/81; ?column? ---------- 9 (1 row) ``` Absolute Value Operator: ```sql > select @ -23.4; ?column? ---------- 23.4 (1 row) ``` ================================================ FILE: postgres/storing-emails-with-citext.md ================================================ # Storing Emails With citext Email addresses should be treated as case-insensitive because they are. If a user is trying to sign in with their email address, we shouldn't care if they type `user@example.com` or `User@example.com`. Both of those email addresses should be treated as equal and ultimately lead us to the same `User` record. With the [`citext`](http://www.postgresql.org/docs/current/static/citext.html) extension, we can create a column that acts as a case-insensitive text type. Any comparisons on a column of that type will internally have the `lower` function executed on the arguments. The following example shows this in action: ```sql create extension if not exists citext; create table citext_emails ( id serial primary key, email citext not null unique ); insert into citext_emails (email) values ('LizLemon@nbc.com'); select * from citext_emails where email = 'lizlemon@nbc.com'; -- id | email -- ----+------------------ -- 1 | LizLemon@nbc.com ``` See [`citext-emails.sql`](https://github.com/jbranchaud/postgresing/blob/master/citext-emails.sql) for a full example. ================================================ FILE: postgres/string-contains-another-string.md ================================================ # String Contains Another String You can check if a string *contains* another string using the `position` function. ```sql > select position('One' in 'One Two Three'); position ---------- 1 ``` It returns the 1-based index of the first character of the first match of that substring. ```sql > select position('Four' in 'One Two Three'); position ---------- 0 ``` If the substring doesn't appear within the string, then the result is 0. Thus, you can determine if a string *contains* another string by checking if the value resulting from `position` is greater than 0. ================================================ FILE: postgres/survey-of-user-defined-ordering-of-records.md ================================================ # Survey Of User-Defined Ordering Of Records ### Approaches #### Positional Indexes Assign an integer position to everything in the list. To insert something in the middle of the list, you assign it that position and then increment the position each item in the list after it by one. Can require a lot more writes than other approaches. #### Positional Averaging You can insert an item between two others with a single operation by taking the average of the positional values (indexes) of the two surrounding items and making that the position of the item being inserted. - Decimal / Floating-Point - runs out of precision fairly quickly - Fractional / True Fractions - much more precision, trickier to implement - Large Int Boundaries - add first item at `0`, add second item at average of `0` and `MAX_INT`. Need to insert at the front of the list, average lowest index item with `MIN_INT`. [source](https://github.com/brendon/ranked-model/blob/master/lib/ranked-model.rb#L8-L11) #### LexoRank Similar to the (numeric) positional averaging approaches, but uses strings instead to get much more precision. Lexicographical ordering is used. To insert between two items, add a character to the end of the string that will position it between the two items. #### Stored Array Store an ordered array of element IDs either as a `jsonb` column or stringified JSON in a `text` column on the _collection_ table. If you have `list_items` that are ordered, then the `lists` table will have an `ordering` column of `list_items` IDs. You lose referential integrity with this approach. Here is an [example implementation](https://gist.github.com/jbranchaud/a00e0d6d17d562bec3007e3a4bcace94). #### Linked List A singly or doubly linked list where each item points to the next item in the list (and in the case of the doubly, points back to its previous item). [source](https://news.ycombinator.com/item?id=25802129) ### References - [User-defined Order in SQL](https://begriffs.com/posts/2018-03-20-user-defined-order.html) - [Building Sortable Relations with PostgreSQL](https://brunoscheufler.com/blog/2022-09-26-building-sortable-relations-with-postgresql) - [User Defined Ordering Made Easy](https://steve.dignam.xyz/2020/03/31/practical-ordering/) - [Realtime Editing of Ordered Sequences](https://www.figma.com/blog/realtime-editing-of-ordered-sequences/#fractional-indexing/) (fractional indexing) - [Numeric and String Indexing](https://stackoverflow.com/questions/9536262/best-representation-of-an-ordered-list-in-a-database/49956113#49956113) - [Keeping an ordered collection in PostgreSQL](https://medium.com/the-missing-bit/keeping-an-ordered-collection-in-postgresql-9da0348c4bbe) - [Jira’s ranking system explained](https://tmcalm.nl/blog/lexorank-jira-ranking-system-explained/) - [JIRA LexoRank Explained](https://www.youtube.com/watch?v=OjQv9xMoFbg) - [User-defined Order in SQL | Hacker News](https://news.ycombinator.com/item?id=16635440) ### Examples / Implementations - [Ranked Model - big integer positional averaging](https://github.com/brendon/ranked-model/blob/master/lib/ranked-model/ranker.rb#L174-L181) - [LexoRank Ruby Gem](https://github.com/richardboehme/lexorank) ================================================ FILE: postgres/switch-non-castable-column-type-with-using-clause.md ================================================ # Switch Non-Castable Column Type With Using Clause With certain data types, such as from `int` to `bigint` or `timestamptz` to `timestamp`, there is an automatic type casting that can take place with existing data. This means Postgres knows how to handle a data type change like: ```sql alter table users alter column id set data type bigint; ``` With other data types, such as `int` to `uuid`, there is no way for Postgres to know how to automatically cast it. To change the data type of a column in a scenario like this, you have to tell Postgres how to handle the conversion with a `using` clause. ```sql alter table users alter column id set data type uuid using (gen_random_uuid()); ``` In this instance, the `using` clause tells Postgres to ignore the existing integer `id` value and use the `gen_random_uuid()` function to generate a UUID value to take its place. The `using` clause can also reference the existing column value as part of its type cast. See the [alter table documentation](https://www.postgresql.org/docs/current/sql-altertable.html) for more details on this. ================================================ FILE: postgres/switch-the-running-postgres-server-version.md ================================================ # Switch The Running Postgres Server Version I use [asdf](https://github.com/asdf-vm/asdf) install and manage multiple versions of Postgres on my Mac OSX machine. With `asdf` and project-based `.tools-versions` files, I can control what version of Postgres (`psql`) I use at a project-level. The one snag with this workflow is managing the currently running server version. Lets say I need to switch from a project using `12.3` to a project using `13.1`. If the Postgres server running on my machine is using the Postgres server 12.3, then I'll need to manually stop that server and start up the Postgres server 13.1. This can be done like so: ```bash # stop the 12.3 server $ $HOME/.asdf/installs/postgres/12.3/bin/pg_ctl \ -D $HOME/.asdf/installs/postgres/12.3/data \ stop # start the 13.1 server $ $HOME/.asdf/installs/postgres/13.1/bin/pg_ctl \ -D $HOME/.asdf/installs/postgres/13.1/data \ start ``` This uses the specific asdf-versioned `pg_ctl` command to stop and start the servers. I've found it tedious to dig up these commands each time I need to switch, so I added a [`switch_pg` function to my `~/.zshrc` config](https://gist.github.com/jbranchaud/3cda6be6e1dc69c6f55435a387018dac). ================================================ FILE: postgres/table-names-are-treated-as-lower-case-by-default.md ================================================ # Table Names Are Treated As Lower-Case By Default This one is a bit unintuitive and can cause some real confusion -- when you create a table in PostgreSQL, any casing is ignored, it is treated as lower-case. Let's see it to believe it: ```sql > create table BookMarks ( id integer generated always as identity primary key, location text not null ); > \d +--------+--------------------+----------+----------+ | Schema | Name | Type | Owner | |--------+--------------------+----------+----------| | public | bookmarks | table | postgres | | public | bookmarks_id_seq | sequence | postgres | +--------+--------------------+----------+----------+ ``` Notice that when we list our tables, the uppercase `M` and `B` are gone. That's because Postgres folds away the casing when processing the table name identifier. It doesn't matter how we refer to it for queries: ```sql > select * from BookMarks; +----+----------+ | id | location | |----+----------| +----+----------+ > select * from bookmarks; +----+----------+ | id | location | |----+----------| +----+----------+ ``` You can force Postgres to respect the casing by wrapping the table name in quotes. ```sql > create table "BookMarks" ( id integer generated always as identity primary key, location text not null ); > \d +--------+--------------------+----------+----------+ | Schema | Name | Type | Owner | |--------+--------------------+----------+----------| | public | BookMarks | table | postgres | | public | BookMarks_id_seq | sequence | postgres | +--------+--------------------+----------+----------+ > select * from "BookMarks"; +----+----------+ | id | location | |----+----------| +----+----------+ > select * from "bookmarks"; relation "bookmarks" does not exist LINE 1: select * from "bookmarks" ^ > select * from BookMarks; relation "bookmarks" does not exist LINE 1: select * from BookMarks ^ ``` That then means you have to quote your table name anytime you want to refer to it in a query. It's not worth it. It is better to always keep your table names lower-case using snake case. [source](https://weiyen.net/articles/avoid-capital-letters-in-postgres-names) ================================================ FILE: postgres/temporarily-disable-triggers.md ================================================ # Temporarily Disable Triggers In general, you are always going to want your triggers to fire. That's why they are there. Though special circumstances may arise where you need to temporarily disable them. Use ```sql > set session_replication_role = 'replica'; SET ``` By changing the [replication role](http://www.postgresql.org/docs/9.4/static/runtime-config-client.html#GUC-SESSION-REPLICATION-ROLE) from `origin` to `replica` you are essentially disabling all non-replica triggers across the database (for that session). When you are done, you can simply set the replication role back so that normal trigger behavior can resume ```sql > set session_replication_role = 'origin'; SET ``` A more direct and fine-grained approach to disabling triggers is to use an `alter table` command that targets a specific trigger. h/t Jack Christensen ================================================ FILE: postgres/temporary-tables.md ================================================ # Temporary Tables Create a temporary table in Postgres like so ```sql create temp table posts ( ... ); ``` This table (and its data) will only last for the duration of the session. It is created on a schema specific to temporary tables. It is also worth noting that it won't be autovacuumed, so this must be done manually as necessary. ================================================ FILE: postgres/terminating-a-connection.md ================================================ # Terminating A Connection Consider the scenario where you are trying to drop a database, but there are existing connections. ```bash $ dropdb sample_db dropdb: database removal failed: ERROR: database "sample_db" is being accessed by other users DETAIL: There is 1 other session using the database. ``` If you don't know where these connections are, you can terminate them within a `psql` session. You just have to figure out the `pid` of those connections. In [List Connections To A Database](list-connections-to-a-database.md), I explained how to get at the `pid` values of connections. Using the `pid` value and `pg_terminate_backend()`, you can terminate a connection. ```sql > select pg_terminate_backend(12345); pg_terminate_backend ---------------------- t ``` To terminate all connections to a particular database, use a query like the following: ```sql select pg_terminate_backend(pg_stat_activity.pid) from pg_stat_activity where pg_stat_activity.datname = 'sample_db' and pid <> pg_backend_pid(); pg_terminate_backend ---------------------- t ``` This excludes the current session, so you'll need to exit `psql` as well before dropping the database. [source](http://stackoverflow.com/questions/5408156/how-to-drop-a-postgresql-database-if-there-are-active-connections-to-it) ================================================ FILE: postgres/the-nullif-function.md ================================================ # The nullif Function PostgreSQL, in addition to generalized case statements, includes the [`nullif`](https://www.postgresql.org/docs/current/functions-conditional.html) function. The docs describe it as a way "to perform the inversation operation of a `coalesce`". Rather than resolving to some fallback value if the primary value is `null` (like `coalesce` does), it will resolve to `null` if the given values are the same. ```sql > select nullif(0, 0); nullif -------- ø (1 row) ``` If the values are not equal, then the first value is the result of the function. ```sql > select nullif(1, 0); nullif -------- 1 (1 row) ``` One way this can be used is in conjunction with the `coalesce` function. For instance, if I have a table of values that are either 0 or a positive number, I can coerce all the zeros to be `1` like so. ```sql > select coalesce(nullif(0, 0), 1); coalesce ---------- 1 (1 row) ``` h/t [Ian Jones](https://twitter.com/_jonesian) ================================================ FILE: postgres/timestamp-functions.md ================================================ # Timestamp Functions There are a handful of timestamp functions available in postgres. The most common one is probably `now()`. This is an alias of `transaction_timestamp()` which the postgres docs describe as: > Current date and time (start of current transaction) Two other interesting timestamp functions are `statement_timestamp()` and `clock_timestamp()`. The postgres docs describe `statement_timestamp()` as: > Current date and time (start of current statement) Using `statement_timestamp()` throughout a transaction will yield different results from statement to statement. The postgres docs describe `clock_timestamp()` as: > Current date and time (changes during statement execution) Using `clock_timestamp()` may even yield different results depending on where it appears in a given statement. Try running something like this to see: ```postgresql > select clock_timestamp(), clock_timestamp(), clock_timestamp(), clock_timestamp(); clock_timestamp | clock_timestamp | clock_timestamp | clock_timestamp -------------------------------+-------------------------------+-------------------------------+------------------------------ 2015-03-20 14:58:49.832592-05 | 2015-03-20 14:58:49.832592-05 | 2015-03-20 14:58:49.832593-05 | 2015-03-20 14:58:49.832593-05 ``` You'll notice that we see a change in the clock time at the microsecond level mid-way through the statement. sources: [postgres docs](http://www.postgresql.org/docs/9.1/static/functions-datetime.html) and [Jack C.](http://hashrocket.com/team/jack-christensen) ================================================ FILE: postgres/toggling-the-pager-in-psql.md ================================================ # Toggling The Pager In PSQL When the pager is enabled in `psql`, commands that produce larger output will be opened in a pager. The pager can be enabled within `psql` by running `\pset pager on`. If you'd like to retain the output of commands, perhaps as reference for subsequent commands, you can turn the pager off. As you might expect, the pager can be disabled with `\pset pager off`. [source](http://stackoverflow.com/questions/11180179/postgresql-disable-more-output) ================================================ FILE: postgres/track-psql-history-separately-per-database.md ================================================ # Track psql History Separately Per Database By default, `psql` will keep track of all recent queries and commands in the `.psql_history` file in your home directory. When in a `psql` session, you can hit the `Up` key to go back through the history to find a previously entered query. That means you can quickly retrieve and rerun past queries. However the default `psql` configuration means that your history can contain queries from a `psql` session with another database that don't make sense in the context of the current database. You can keep these query histories separate by configuring `psql` to use separate history files per database. This can be done by adding the following line to your `~/.psqlrc` file. ``` \set HISTFILE ~/.psql_history-:DBNAME ``` [source](https://github.com/hashrocket/dotmatrix/commit/1bd581db3a7192eb7aaa766a97e4b4b82d544067) ================================================ FILE: postgres/trim-leading-and-trailing-space-from-string.md ================================================ # Trim Leading And Trailing Space From String PostgreSQL has a bunch of [string functions](https://www.postgresql.org/docs/current/functions-string.html), including several for doing various string trimming. We can use the simplest form of `trim` to remove leading and trailing space characters from a string. ```sql > select trim(' Taco Cat '); +----------+ | btrim | |----------| | Taco Cat | +----------+ ``` The syntax for calling `trim` is a bit odd relative to other PostgreSQL functions and functions in other languages. Here is the "grammar" as described in the docs: ``` trim ( [ LEADING | TRAILING | BOTH ] [ characters text ] FROM string text ) → text ``` We pick `leading`, `trailing`, or `both`, with `both` being the default. Then we specify the character(s) we want to remove. This is also optional, the default being the space character. Then we say `from` what string we want to trim those characters. Here we remove all sequential spaces from `both` ends of the given string: ```sql > select trim(both from ' Taco Cat '); +----------+ | btrim | |----------| | Taco Cat | +----------+ ``` To further demonstrate how `trim` works, here we remove all sequences made up of any of spaces, uppercase `T`, and lowercase `t` from `both` ends of the string: ```sql > select trim(both ' Tt' from ' Taco Cat '); +--------+ | btrim | |--------| | aco Ca | +--------+ ``` Notice that in all the above examples the column name of the result is `btrim`. That's probably because `btrim` (_trim both ends_) is being called under the hood for the `both` option. ================================================ FILE: postgres/truncate-all-rows.md ================================================ # Truncate All Rows Given a postgres database, if you want to delete all rows in a table, you can use the `DELETE` query without any conditions. ```sql > delete from pokemons; DELETE 151 ``` Though `DELETE` can do the job, if you really are deleting all rows to clear out a table, you are better off using `TRUNCATE`. A `TRUNCATE` query will be faster than a `DELETE` query because it will just delete the rows without scanning them as it goes. ```sql > truncate pokemons; TRUNCATE TABLE ``` [source](http://www.postgresql.org/docs/8.2/static/sql-truncate.html) ================================================ FILE: postgres/truncate-tables-with-dependents.md ================================================ # Truncate Tables With Dependents In [Truncate All Rows](truncate-all-rows.md), I talked about how postgres's `truncate` can be used to quickly delete all rows in a table. In practice this alone won't be very useful though, because tables usually have other tables that depend on them via foreign keys. If you have tables `A` and `B` where `B` has a foreign key referencing `A`, then trying to truncate `A` will result in something like this: ```sql > truncate A; ERROR: cannot truncate a table referenced in a foreign key constraint ``` Fortunately, `truncate` has some tricks up its sleeve. If you know two tables are tied together via a foreign key constraint, you can just truncate both of them at once: ```sql > truncate A, B; TRUNCATE TABLE; ``` If many tables are tied together in this way and you are looking to throw all of it out, then a simpler approach is to cascade the truncation: ```sql > truncate A cascade; NOTICE: truncate cascades to table "B" TRUNCATE TABLE ``` Use these with care and potentially within transactions because your data will go bye bye. h/t Dillon Hafer and Jack Christensen ================================================ FILE: postgres/turn-timing-on.md ================================================ # Turn Timing On When digging around your database and running queries, it is helpful to have an eye on the speed of those queries. This can give insight into where there are needs for optimizations. Turn timing on (and off) within `psql` by running `\timing`. With timing on, the duration of each query will be displayed in milliseconds after the output of the query. ================================================ FILE: postgres/two-ways-to-compute-factorial.md ================================================ # Two Ways To Compute Factorial In PostgreSQL, there are two ways to compute the factorial of a number. There is a prefix operator and a postfix operator. The prefix operator is `!!` and can be used like so: ```sql > select !!5; ?column? ---------- 120 ``` The postfix operator is `!` and can be used like so: ```sql > select 5!; ?column? ---------- 120 ``` See the [mathematical functions and operators docs](http://www.postgresql.org/docs/8.1/static/functions-math.html) for more details. ================================================ FILE: postgres/two-ways-to-escape-a-quote-in-a-string.md ================================================ # Two Ways To Escape A Quote In A String String literals in PostgreSQL have to be wrapped in single quotes. This can be tricky if you are faced with writing out a query using a string that contains a single quote. ```sql > select 'who's on first?'; ... ``` The query won't execute because it is waiting for you to close the second set of quotes. I know of two ways to handle this situation. The first is to put two single quotes back to back. The first will cause the second to be escaped so that the quote shows up in the string. ```sql > select 'who''s on first?'; ?column? ----------------- who's on first? (1 row) ``` The second is to prepend the string with [the `E` character](https://www.postgresql.org/docs/current/sql-syntax-lexical.html#SQL-SYNTAX-STRINGS) to allow escape sequences in strings. ```sql > select E'who\'s on first?'; ?column? ----------------- who's on first? (1 row) ``` [source](https://stackoverflow.com/a/12320729) ================================================ FILE: postgres/types-by-category.md ================================================ # Types By Category Postgres has many types, each of which fall into a particular category. These categories include Array, Boolean, String, Numeric, Composite, etc. Each of these categories has a corresponding code. For instance, numeric types have a code of `N`. Using `N` I can get a list of all the numeric types: ```sql > select typname from pg_type where typcategory = 'N'; typname ----------------- int8 int2 int4 regproc oid float4 float8 money numeric regprocedure regoper regoperator regclass regtype regconfig regdictionary cardinal_number (17 rows) ``` Check out [`pg_type`](http://www.postgresql.org/docs/current/interactive/catalog-pg-type.html) in the Postgres docs for a list of all categories and codes. ================================================ FILE: postgres/unable-to-infer-data-type-in-production.md ================================================ # Unable To Infer Data Type In Production Inspired by [You Probably Don't Need Query Builders](https://mattrighetti.com/2025/01/20/you-dont-need-sql-builders), I wrote a query in one of my applications that has filter clauses that get short-circuited if the filter value hasn't been included. That query looked something like this: ```ruby @tags = Tag.where("? is null or normalized_value ilike ?", normalized_query, "%#{normalized_query}%") .order(:normalized_value) .limit(10) ``` The `normalized_value ilike ?` filtering won't be applied if the `normalized_query` value isn't present (`nil`). This helps me avoid writing messy ternaries or if-else conditional query building madness. Unfortunately, when I shipped this query to production, the page started failing and Postgres was reporting this error in the logs. ``` Caused by: PG::IndeterminateDatatype (ERROR: could not determine data type of parameter $1) ``` The query is prepared as a parameterized statement and Postgres appears to be unable to determine the datatype of the first parameter (`$1`) — `normalized_query`. I was unable to reproduce the issue in development. It was only occuring in production. Until I can come up with a root cause analysis, I have the following fix that does a casting to `text`. This helps out with the type inference and makes the issue go away. ```ruby @tags = Tag.where("cast(? as text) is null or normalized_value ilike ?", normalized_query, "%#{normalized_query}%") .order(:normalized_value) .limit(10) ``` Interestingly, this person using `pgtyped` [ran into the exact same issue with the same type of query](https://github.com/adelsz/pgtyped/issues/354). ================================================ FILE: postgres/union-all-rows-including-duplicates.md ================================================ # Union All Rows Including Duplicates Two tables or sets of results can be joined together into a single result set using [the `union` operator](https://www.postgresql.org/docs/current/queries-union.html). When combining results with `union`, all duplicate rows will be removed from its result. ```sql > select generate_series(1,4) union select generate_series(3,6) order by 1 asc; generate_series ----------------- 1 2 3 4 5 6 (6 rows) ``` Notice that despite both sides of the `union` having their own 3 and 4, those values each only show up once in the result. If we don't want duplicates to be excluded, we can use `union all`. ```sql > select generate_series(1,4) union all select generate_series(3,6) order by 1 asc; generate_series ----------------- 1 2 3 3 4 4 5 6 (8 rows) ``` In this case we have 8 rows instead of 6 with the values 3 and 4 each appearing twice. [source](https://www.postgresqltutorial.com/postgresql-union/) ================================================ FILE: postgres/use-a-psqlrc-file-for-common-settings.md ================================================ # Use A psqlrc File For Common Settings There are a handful of settings that I inevitably turn on or configure each time I open up a `psql` session. I can save myself a little time and sanity by configuring these things in a `.psqlrc` dotfile that is located in my home directory. This will ensure my `psql` session is configured just how I like it each time I launch it. Here is what my `~/.psqlrc` file currently looks like: ``` \x auto \timing \pset null 'Ø' ``` ================================================ FILE: postgres/use-a-trigger-to-mirror-inserts-to-another-table.md ================================================ # Use A Trigger To Mirror Inserts To Another Table On a PostgreSQL server, a trigger can be set up to execute a function whenever a certain action happens. In this case, I want set up a trigger to call a custom function whenever an `insert` happens on a specific table (`original_table`). That custom function will then mirror the inserted values into a secondary table (`another_table`). First, I have to create a function that will respond to `insert` operations by inserting the newly inserted rows into `another_table`. ```sql create or replace function mirror_table_to_another_table() returns trigger as $mirrored_table$ begin if (TG_OP = 'INSERT') then insert into another_table select * from new_table; end if; return null; -- result is ignored since this is an after trigger end; $mirrored_table$ language plpgsql; ``` This function can then be referenced by the trigger I set up. After any insert on the `original_table`, the function defined above will be executed. ```sql create trigger mirror_table_to_another_table_trigger after insert on original_table referencing new table as new_table for each statement execute function mirror_table_to_another_table(); ``` Note that I am handling inserts at a statement level and that multiple rows can be inserted in a single statement. That is why the function mirrors to the other table with `select * from new_table`. ================================================ FILE: postgres/use-argument-indexes.md ================================================ # Use Argument Indexes In Postgres, each of the arguments you specify in a `select` statement has a 1-based index tied to it. You can use these indexes in the `order by` and `group by` parts of the statement. Instead of writing ```sql select id, updated_at from posts order by updated_at; ``` you can write ```sql select id, updated_at from posts order by 2; ``` If you want to group by a table's `type` and then order by the counts from highest to lowest, you can do the following ```sql select type, count(*) from transaction group by 1 order by 2 desc; ``` ================================================ FILE: postgres/use-not-valid-to-immediately-enforce-a-constraint.md ================================================ # Use Not Valid To Immediately Enforce A Constraint When adding a constraint to a table, you can optionally include `not valid`. This tells Postgres that it doesn't need to enforce the constraint on existing records in the table. At least not immediately. This constraint will be enforced for any updates and subsequent insertions. Thus, you can immediately enforce the constraint while giving yourself time to clean up or massage any existing records that conflict with the constraint. Here is an example of how you would add a constraint this way: ```sql alter table boxes add constraint check_valid_length check (length > 0) not valid; ``` Eventually, you will want to ensure that all data in the table conforms to the constraint. Once you get to that point, you can mark the constraint as valid with a `validate constraint` command: ```sql alter table boxes validate constraint check_valid_length; ``` As long as all records are valid with respect to this constraint, it will be marked as valid. h/t Chris Erin ================================================ FILE: postgres/use-rename-to-hot-swap-two-tables.md ================================================ # Use Rename To Hot Swap Two Tables The [`alter table`](https://www.postgresql.org/docs/current/sql-altertable.html) command can be used to [rename a table](https://www.postgresqltutorial.com/postgresql-tutorial/postgresql-rename-table/). Because it is changing the name of a reference rather than actually moving any data around, it is very fast. We can exploit the speed of a _rename_ to hot swap two tables. This is useful for a situation where we've created an identical table with a small fraction of the data of the original table. By hot swapping them, we've exchanged the large table for a smaller one without our application code noticing anything happened. Let's assume we have a massive `events` table and then a much smaller `new_events` table with the same structure. The following transaction will swap those two tables in the blink of an eye. ```sql begin; alter table events rename to old_events; alter table new_events rename to events; commit; ``` The resulting `old_events` table can then be deleted at our convenience. The other nice thing about this approach is that, before deleting `old_events`, you can easily and quickly swap them back using the same approach. It is always a comfort when huge changes like this are easy to reverse if necessary. ================================================ FILE: postgres/use-variables-in-an-anonymous-function.md ================================================ # Use Variables In An Anonymous Function I was curious how variables could be declared and used in PostgreSQL, so I did a little experiment with a `books` and `authors` schema. Variables need to be declared and used within the context of a function. Using the [`do` syntax](https://www.postgresql.org/docs/9.1/sql-do.html) I am able to declare and execute an anoymous code block. Within that code block I can declare one or more variables by giving them a type and optionally a default value. Below I declare `author_id` with a default and `result` as a `record` type. ```sql do $$ declare author_id varchar := 'e2b42ebf-7ea9-4d9e-8edf-310fc1894bcd'; result record; begin for result in select title from books where "authorId" = author_id loop raise notice '| % |', result.title; end loop; end $$; ``` I'm able to use the `author_id` variable directly in a `select` statement. ```sql select title from books where "authorId" = author_id ``` and then using a [for loop](https://www.postgresql.org/docs/current/plpgsql-control-structures.html#PLPGSQL-RECORDS-ITERATING) with my `result` of `record` type, I can iterate over each of the results from the `select`. Because this anonymous `do` block implicitly has a `void` return type, I need to do something with the result within the block. For demonstration purposes, I use [`raise notice`](https://www.postgresql.org/docs/current/plpgsql-errors-and-messages.html) to log out each book title. ================================================ FILE: postgres/using-expressions-in-indexes.md ================================================ # Using Expressions In Indexes Though we usually see column names by themselves when defining an index, it is also possible to create an index with an expression. Let's say I have a `users` table with an `email` column. Then I may end up creating an index like this ```sql create index email_idx on users (email); ``` If I always perform queries on the `email` column with the `lower()` function, like this ```sql select * from users where lower(email) = lower('some@email.com'); ``` then I will want to also create an index with that full expression -- `lower(email)` I can do this with a statement like the following ```sql create index lower_email_idx on users (lower(email)); ``` Without an index that uses the full `lower(email)` expression, `select` statements like the one above will be forced to do full sequential scans instead of indexed scans. ================================================ FILE: postgres/using-intervals-to-offset-time.md ================================================ # Using Intervals To Offset Time Postgres Intervals can be used with time as a way of determining a standard offset. For instance, I can concisely determine what the time was 2 hours earlier with ```sql > select now() - '2 hours'::interval as earlier; earlier ------------------------------- 2015-06-12 21:17:43.678822-05 ``` or similarly ```sql > select now() - interval '2 hours' as earlier; earlier ------------------------------- 2015-06-12 21:17:43.678822-05 ``` ================================================ FILE: postgres/who-is-the-current-user.md ================================================ # Who Is The Current User You can determine the current user of a psql session by selecting on the `current_user` ```sql > select current_user; current_user ---------------- test_user ``` You can also select on the `user` which is an alias of `current_user` ```sql > select user; user ---------------- test_user ``` ================================================ FILE: postgres/word-count-for-a-column.md ================================================ # Word Count for a Column Assuming I have a database with a posts table: ```sql > select * from posts where id = 1; id | title | content ----+----------+------------------------------------ 1 | My Title | This is the content of my article. ``` I can compute the word count of the content of a given post like so: ```sql > select sum(array_length(regexp_split_to_array(content, '\s+'), 1)) from posts where id = 1; sum ----- 7 ``` [source](http://blog.lingohub.com/2013/07/sql-word-count-character-count-postgres/) ================================================ FILE: postgres/write-a-query-result-to-file.md ================================================ # Write A Query Result To File Generally when writing a query in `psql` a statement will be terminated with a semicolon. An alternative approach is to end it with a `\g` instead. This will also send the query to the Postgres server for execution. ```sql select 1 \g ``` If a filename is included after the `\g`, then the result of the query will be written to that file instead of output to the `psql` session. ```sql > select 1, 2, 3 \g query_result.txt ``` If we `cat` that file, we can see the query result. ```sql Time: 4.293 ms > \! cat query_result.txt ?column? | ?column? | ?column? ----------+----------+---------- 1 | 2 | 3 (1 row) ``` See `man psql` for more details. ================================================ FILE: prisma/apply-separate-formatting-with-a-blank-line.md ================================================ # Apply Separate Formatting With A Blank Line Prisma's CLI includes a `format` command. When run, this will standardize the layout of the `schema.prisma` file. This is variable based on the length of column and attribute names. You can exercise some control over the formatting and visually organize the parts of a model by injecting blank lines between sets of statements. Here is what a formatted model might look like with no blank lines: ```prisma model Book { id String @id @default(uuid()) title String publicationDate DateTime author Author @relation(fields: [authorId], references: [id]) authorId String createdAt DateTime @default(now()) @map(name: "created_at") updatedAt DateTime @updatedAt @map(name: "updated_at") } ``` All the types get aligned based on the longest column name. Now, here is what it looks like if I use a blank line to separate all the primary fields from the `createdAt` and `updatedAt`. ```prisma model Book { id String @id @default(uuid()) title String publicationDate DateTime author Author @relation(fields: [authorId], references: [id]) authorId String createdAt DateTime @default(now()) @map(name: "created_at") updatedAt DateTime @updatedAt @map(name: "updated_at") } ``` Notice how the second block of lines is aligned independent of the first. ================================================ FILE: prisma/batch-insert-records-with-create-many.md ================================================ # Batch Insert Records With createMany As part of its suite of CRUD functionality, [Prisma has a `createMany` function](https://www.prisma.io/docs/reference/api-reference/prisma-client-reference#createmany) that allows you to `insert` many records at once with your target database. This will perform one large insert statement which will generally be faster than an equivalent series of individual insert statements. ```javascript const createResult = await prisma.books.createMany({ data: [ { isbn: '123', title: 'The Goldfinch' }, { isbn: '345', title: 'Piranesi' }, { isbn: '987', title: 'The Fifth Season' }, ], skipDuplicates: true }) ``` With the `skipDuplicates` option, any inserts that would result in a duplicate record (`isbn` is my unique key in this example) will be skipped. The result of the query will include a `count` key to let you know how many records were actually inserted. If I'm bulk inserting a _ton_ of data, I like to chunk it up so that I'm not creating queries that are too big. For a recent script, I found that `1000` was a good chunking number. ```javascript import 'chunk' from 'lodash/chunk' const chunkedBatchInsert = async (records) => { for(const batch of chunk(records, 1000)) { await prisma.books.createMany({ data: batch, skipDuplicates: true }) } } ``` ================================================ FILE: prisma/check-if-database-and-schema-are-not-in-sync.md ================================================ # Check If Database And Schema Are Not In Sync The [`prisma migrate diff`](https://www.prisma.io/docs/orm/reference/prisma-cli-reference#migrate-diff) command is a versatile tool that can be used to check if there is a difference between two sources. In this case, we want to check if our database is in sync with the `schema.prisma` file for our project. If we have made changes to the schema file, but haven't yet migrated or pushed those changes to our local database, then we want to be notified of that mismatch. We'll point at the schema file with `--to-schema-datamodel` and at our local database with `--from-url`. ```bash ❯ npx prisma migrate diff \ --to-schema-datamodel ./prisma/schema.prisma \ --from-url mysql://root@localhost:3309/kcd-products [*] Changed the `User` table [+] Added column `metadata` ``` In the case where there is a different, we see an output summary of the diff. Let's say we've applied our changes (`prisma db push`) to our local database. If we now run that same command again, we can see that no difference is detected and our database is in sync with our schema. ```bash ❯ npx prisma migrate diff \ --to-schema-datamodel ./prisma/schema.prisma \ --from-url mysql://root@localhost:3309/kcd-products No difference detected. ``` ================================================ FILE: prisma/configure-client-to-log-sql-queries.md ================================================ # Configure Client To Log SQL Queries During development, especially while debugging, it can be helpful to see the actual SQL queries generated by Prisma queries we are formulating. Because an ORM is an abstraction over SQL, it isn't always obvious what the resulting SQL will turn out to be. By adding the `log` configuration to where we initialize our Prisma client, we can tell it to log things like errors and the SQL of the queries it executes. ```javascript export const prisma = new PrismaClient({ log: ['error', 'query'] }) ``` If we only want the SQL logged in development, we could do something like this: ```javascript export const prisma = new PrismaClient({ log: process.env.NODE_ENV === 'development' ? ['query', 'error'] : ['error'], }) ``` If we look in our app server logs when a code path that contains a Prisma query executes, we'll see a line for each SQL statement. [source](https://github.com/prisma/prisma/discussions/3967) ================================================ FILE: prisma/execute-a-raw-sql-query.md ================================================ # Execute A Raw SQL Query [Prisma](https://www.prisma.io/) with TypeScript acts as a powerful [ORM](https://en.wikipedia.org/wiki/Object%E2%80%93relational_mapping) for interacting with your database. However, not every kind of query that you may need can be represented with the API generated from your schema. For instance, certain tables might be ignored in your `prisma.schema` file. Or you may want to hand-craft a query for performance or ergonomics reasons. Like any good ORM, Prisma provides an escape hatch for this kind of situation with the [`$queryRaw`](https://www.prisma.io/docs/concepts/components/prisma-client/raw-database-access#queryraw) tag function. ```typescript function getExpiresIn({ email }) { const prisma = new PrismaClient() const result: Array = await prisma.$queryRaw` select id, code, date_trunc('days', expires_at - now())::varchar as expires_in from tickets where email = ${email} ` // result // => [{ id: 123, code: 'abc123', expires_in: '3 days' }] return result } ``` This runs the raw SQL in the template literal against the database. The result is returned as an array of objects with key-value pairs for each selected value. Writing the SQL query myself, in this case, means I can take advantage of database (Postgres) specific features (e.g. [`date_trunc`](https://www.postgresqltutorial.com/postgresql-date-functions/postgresql-date_trunc/) and [interval math](https://www.postgresqltutorial.com/postgresql-tutorial/postgresql-interval/)). [source](https://www.prisma.io/docs/concepts/components/prisma-client/raw-database-access) ================================================ FILE: prisma/grab-a-limited-set-of-records.md ================================================ # Grab A Limited Set Of Records Let's say you want to grab some records from a table, but you want to limit the result set to 10 records. You can do that with the `take` option. ```javascript const posts = await prisma.post.findMany({ take: 10 }); ``` It is generally good to not assume anything about the ordering. Instead, you should be explicit about the order you want, so let's include an `orderBy` as well. ```javascript const posts = await prisma.post.findMany({ take: 10, orderBy: { createdAt: "asc" }, }); ``` This will return the 10 most recently created posts. [source](https://www.prisma.io/docs/reference/api-reference/prisma-client-reference#findmany) ================================================ FILE: prisma/open-connections-to-multiple-databases.md ================================================ # Open Connections To Multiple Databases A standard database connection with Prisma is determined by a `DATABASE_URL` env var set in the `.env` file of your project. Typically, the Prisma client connecting to that URL will be configured in a separate file and imported wherever it is used. ```javascript import {prisma} from './utils/prisma' ``` What if you want to connect to a second, alternate database? You can create a new Prisma client with the data source configured to be a different connection URL. ```javascript import {prisma as primaryPrismaClient} from '@skillrecordings/database' import {PrismaClient} from '@prisma/client' const secondaryDatabaseUrl = 'mysql://root@localhost:3399/my-database' const secondaryPrismaClient = new PrismaClient({ datasources: { db: { url: secondaryDatabaseUrl } }, }) ``` And with that, you can execute queries against both databases. ```javascript const primaryUserCount = await primaryPrismaClient.user.count() const secondaryUserCount = await secondaryPrismaClient.user.count() ``` [source](https://www.prisma.io/docs/reference/api-reference/prisma-client-reference#programmatically-override-a-datasource-url) ================================================ FILE: prisma/override-table-name-for-prisma-model.md ================================================ # Override Table Name For Prisma Model When defining your Prisma schema, you'll add models to your `prisma/schema.prisma` file that look something like this: ``` model Book { id BigInt @id @default(autoincrement()) @db.BigInt title String author String publication_year Int created_at DateTime @default(now()) updated_at DateTime @updatedAt } ``` The prisma client (ORM-layer) that gets generated will have a `Book` type and you'll be able to reference the model to, for instance, create a record with `prisma.book.create(...)`. Both of these things are derived from the model name: `Book`. The other thing that is derived from the model name is the name given to the underlying database table. So you end up with a table called `Book`. You may, however, prefer a table naming convention where this one would be named `books` (snake_case and pluralized). To achieve that, you have to manually override the table name with [the `@@map` directive](https://www.prisma.io/docs/orm/reference/prisma-schema-reference#map-1). Add it toward the bottom of the model like so: ``` model Book { id BigInt @id @default(autoincrement()) @db.BigInt title String author String publication_year Int created_at DateTime @default(now()) updated_at DateTime @updatedAt @@map("books") } ``` ================================================ FILE: prisma/specify-alternate-location-for-prisma-schema.md ================================================ # Specify Alternate Location For Prisma Schema By default, Prisma looks for a schema in one of two locations: - `./prisma/schema.prisma` - `./schema.prisma` If it isn't in one of those two spots, then you'll get an error. You can manually specify an alternate location. One way is to use the `--schema` flag with all `prisma` commands. A less tedious approach ([introduced in this PR](https://github.com/prisma/prisma/pull/3566)) is to specify the location in your `package.json`. ```json { "prisma": { "schema": "../../packages/database/prisma/schema.prisma" } } ``` This is handy in situations where your database schema and utils are packaged up separately, like in a monorepo. Here is an example of [a monorepo referencing a prisma schema in a separate package](https://github.com/skillrecordings/products/blob/b10dece7170abcb9076221c0863549e2291541ae/apps/testingaccessibility/package.json#L201-L203). [source](https://www.prisma.io/docs/concepts/components/prisma-schema#prisma-schema-file-location) ================================================ FILE: python/access-instance-variables.md ================================================ # Access Instance Variables You can define instance variables when instantiating a class. ```python class Person: def __init__(self, first_name, last_name): self.first_name = first_name self.last_name = last_name def full_name(self): return self.first_name + " " + self.last_name ``` Then those instance variables can be accessed as properties of that class instances. ```python me = Person("Josh", "Branchaud") print(me.first_name) #=> "Josh" print(me.full_name()) #=> "Josh Branchaud" ``` ================================================ FILE: python/access-most-recent-return-value-in-repl.md ================================================ # Access Most Recent Return Value In REPL One of my favorite features of Ruby's `irb` and `pry` are that you can use `_` to reference the most recent return value. Often as we use an interpreter or REPL, we end up with _intermediate_ values. That is, we've execute some kind of statement which returned a value and we now want to use that resulting value in our next statement. Python also supports `_`. Let's say I've run a statement that took a while to process, but I forgot to assign it to a variable. Instead of re-running the whole thing, I can create a variable that references the previous return value using `_`. ```python >>> BytePairEncoding.train_bpe(long_text) {'merge_rules': [...], 'vocab': {...}} >>> result = _ >>> list(result.keys()) ['merge_rules', 'vocab'] ``` Even if I don't necessarily want to assign it a variable, it can be nice to reference the previous value as I continue with what I'm doing: ```python >>> result['merge_rules'][0][1] 256 >>> result['vocab'][_] b'e ' ``` Notice how the value from the first statement gets used as part of a `dict` access. [source](https://docs.python.org/3/tutorial/introduction.html#numbers) ================================================ FILE: python/break-debugger-on-first-line-of-program.md ================================================ # Break Debugger On First Line Of Program One of the things I appreciate about how [Delve](https://github.com/go-delve/delve) (the debugger for Go) works by default is that when you start it up, it immediately breaks on the first line of the program. This is as good a starting point as any regardless of whether you have other breakpoints set. As I was reading through the VS Code Python Debugger configuration docs, I noticed [an option called `stopOnEntry`](https://code.visualstudio.com/docs/python/debugging#_stoponentry). It is turned off by default, but if you turn it on, then you get the same behavior as I described for Delve. Nice! This can be configured in a `.vscode/launch.json` file: ```json { "version": "0.2.0", "configurations": [ { "name": "Python Debugger: Current File", "type": "debugpy", "request": "launch", "program": "${file}", "console": "integratedTerminal", "stopOnEntry": true } ] } ``` Now, when running this debugger configuration profile, you'll break the debugger on the first line of the program. From there add more breakpoints, start stepping through, etc. ================================================ FILE: python/check-if-package-is-installed-with-pip.md ================================================ # Check If Package Is Installed With Pip I recently installed PyTorch, but when I tried using it, I was getting an error about `numpy` not being installed. I was kind of surprised by that because I thought I would have already had that. I wanted to check, so I asked with `pip show`: ```bash ❯ python3 -m pip show numpy WARNING: Package(s) not found: numpy ``` I can even list everything that is installed with `pip` using `pip list` like so: ```bash ❯ python3 -m pip list Package Version Build ------------------ --------- ----- certifi 2026.1.4 cffi 2.0.0 charset-normalizer 3.4.4 click 8.3.1 commonmark 0.9.1 cryptography 46.0.3 docutils 0.22.4 filelock 3.24.2 fsspec 2026.2.0 idna 3.11 Jinja2 3.1.6 ... ``` I then installed `numpy` (`python3 -m pip install numpy`) and how I can use `pip show` again to confirm that. ```bash ❯ python3 -m pip show numpy Name: numpy Version: 2.4.2 Summary: Fundamental package for array computing in Python Home-page: https://numpy.org Author: Travis E. Oliphant et al. Author-email: License-Expression: BSD-3-Clause AND 0BSD AND MIT AND Zlib AND CC0-1.0 Location: /Users/lastword/.local/share/mise/installs/python/3.12.12/lib/python3.12/site-packages Requires: Required-by: ``` ================================================ FILE: python/control-passing-of-time-in-tests.md ================================================ # Control Passing Of Time In Tests While it is nice to be able to write pure functional code, our software still lives in the real world and may have to relate to or depend on the passing of time. In order to test this kind of code, we need time to behave in a reliable, deterministic way. One of the best ways to create a testing environment where that is true is to bring in tooling that hijacks time. The [`freezegun` module](https://github.com/spulec/freezegun) is a great tool for that job. We can use it to freeze time at a specific testable point, advance time a specific amount, and much more. Here is an example from the tests for [my CLI-based time tracking app](https://github.com/jbranchaud/py-vmt/blob/acb26e4840279d936a12f16c505ca7e75e9a6d20/tests/src/py_vmt/test_cli.py#L21) where I freeze time before starting a session. That gives me a chance to assert about the exact start time that is output by the command. Then I can advance time a little and assert that the `status` command outputs the correct thing. ```python import datetime from freezegun import freeze_time # some other test setup omitted ... initial_datetime = datetime.datetime( 2026, 3, 14, 15, 5, 11, 0, datetime.timezone.utc ) with freeze_time(initial_datetime) as frozen_datetime: # start a session start_result = runner.invoke(cli, ["start", "my-project"]) output = "Started tracking 'my-project' at 10:05AM" assert output in start_result.output frozen_datetime.tick(delta=datetime.timedelta(minutes=30)) # check status status_result = runner.invoke(cli, ["status"]) output = "Tracking 'my-project' for 30m (since 10:05AM)" assert output in status_result.output ``` ================================================ FILE: python/create-a-dummy-dataframe-in-pandas.md ================================================ # Create A Dummy DataFrame In Pandas [Pandas](https://pandas.pydata.org/pandas-docs/stable/index.html) has all kinds of utilities for pulling in and processing tabular data. You can pull in a bunch of data from a SQL database into a `DataFrame`. This `DataFrame` object is then something you could pass around, process, and read from. When you are sketching out an implementation or writing some tests, it may not be feasible to read data from a DB. Instead, you can create a little dummy `DataFrame` using the [`from_dict`](https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.from_dict.html#pandas.DataFrame.from_dict) function. ```python import pd data = {'pokemon': ['Charmander', 'Squirtle', 'Bulbasaur'], 'type': ['Fire', 'Water', 'Grass']} pd.DataFrame.from_dict(data) ``` This creates a two column `DataFrame` with a `pokemon` header and a `type` header. The two lists of value will be matched up positionally, so `squirtle` will be paired with `water`. ================================================ FILE: python/create-a-range-of-descending-values.md ================================================ # Create A Range Of Descending Values A typical use of `range` looks something like this: ```python >>> list(range(1, 5)) [1, 2, 3, 4] ``` Which is equivalent to this one where we give a `step` value of `1`. ```python >>> list(range(1, 5, 1)) [1, 2, 3, 4] ``` If we try to create a _negative range_, that is, a range of values in decreasing order, we get an empty list. ```python >>> list(range(0, -7)) [] ``` That's because the `step` value still defaults to `1`. And there are no positive steps between `0` and `-7`. So, let's give `range` a `step` value of `-1`. ```python >>> list(range(0,-7, -1)) [0, -1, -2, -3, -4, -5, -6] ``` One practical use case of a negative range like this is using a list comprehension to transform it into a list of the _last seven days_. ```python >>> from datetime import datetime, timedelta >>> [datetime.now().date() + timedelta(days=days) for days in range(0,-7, -1)] [datetime.date(2026, 3, 19), datetime.date(2026, 3, 18), datetime.date(2026, 3, 17), datetime.date(2026, 3, 16), datetime.date(2026, 3, 15), datetime.date(2026, 3, 14), datetime.date(2026, 3, 13)] ``` Of course this could have been written with a positive range and then subtracting the `timedelta`. I like that I have the option of doing this in whatever way makes the code most readable. ================================================ FILE: python/dunder-methods.md ================================================ # Dunder Methods Python has all kinds of special, or rather, _magic_ methods that allow for customizing all kinds of class behavior. There is `__init__()`, `__bool__()`, and so many others. The thing they all have in common is that their names are wrapped in _double underscores_. This is why they are called _dunder methods_. Some of these are used every single day, like the `__init__()` method for defining how a class should create an object. Others, used from time to time, are for overriding how comparisons or conversions happen. E.g. you may want to override `__bool__()` or `__len__()` to customize the truthiness of a custom class. There are so many others, ones you probably haven't even heard of. To see a full listing, check out this [cheat sheet of every dunder method](https://www.pythonmorsels.com/every-dunder-method/#cheat-sheet). Note: these are not to be confused with _dunder attributes_ which are things like `__name__`, `__file__`, and `__version__` which correspond to a value that you can access in a specific context rather than behavior to override. ================================================ FILE: python/easy-key-value-aggregates-with-defaultdict.md ================================================ # Easy Key-Value Aggregates With defaultdict The `collections` module has the `defaultdict` object that can be used to aggregate values tied to a key. What sets this apart from simply using a `dict` is that we get the base value for free. So if our aggregate value is a list, then we get `[]` by default for each new key. In the same way, we'd get `0` if it was constructed with `int`. Here is the counter example from [Keep A Tally With collections.Counter](keep-a-tally-with-collections-counter.md) ```python from collections import defaultdict def get_pair_counts(token_ids: list[int]) -> Counter: """Count how often each adjacent pair appears""" counts = defaultdict(int) for i in range(len(token_ids) - 1): pair = (token_ids[i], token_ids[i + 1]) counts[pair] += 1 return counts ``` We never have to initially set a key to `0`. If the key is not yet present, then `int()` (the zero-value constructor) is used as the `__missing__` value. We can do the same with `list`: ```python >>> import collections >>> stuff = collections.defaultdict(list) >>> stuff['alpha'].append(1) >>> stuff['alpha'] [1] >>> stuff['beta'] [] ``` In the same way, this uses `list()` as the `__missing__` value to start of each key with an `[]`. I find this so handy because in other languages I've typically had to do something more like this: ```python words_by_length = {} for item in items: if len(item) not in words_by_length: words_by_length[len(item)] = [] words_by_length[len(item)].append(item) ``` This is much clunkier. ================================================ FILE: python/install-with-pip-for-specific-interpreter.md ================================================ # Install With PIP For Specific Interpreter The `pip` module can be invoked for any of its commands, such as install, using a specific Python interpreter like so: ```bash $ python3 -m pip install black ``` This avoid ambiguity between the version of Python I am using and version of the package manager I'm using. Similarly if I need to upgrade `pip`, I can do the following: ```bash $ python3 -m pip install --upgrade pip ``` ================================================ FILE: python/iterate-first-n-items-from-enumerable.md ================================================ # Iterate First N Items From Enumerable As I'm working through the 2nd chapter of [Build a Large Language Model (from scratch)](https://still.visualmode.dev/blogmarks/227), I came across a code example processing a dictionary of words. This example used a for loop to print out each dictionary entry until an index of 50 was reached on then it did a `break`. This struck me as an odd way to grab and process N items from a list. I did some searching and found `itertools` which provides [`islice`](https://docs.python.org/3/library/itertools.html#itertools.islice). ```python from itertools import islice # preprocess words from a file into a word list all_words = ... # not shown here vocab = {token: integer for integer, token in enumerate(all_words)} for item in islice(enumerate(vocab.items()), 50): print(item) ``` The `islice` function is a better approach because the intention (to grab the first 50 things) is encoded in the function call rather than buried in a loop body. It also has equivalent memory efficiency to the original example because it lazily processes the list of `vocab` items. ================================================ FILE: python/iterate-over-a-dictionary.md ================================================ # Iterate Over A Dictionary Let's say we have a `dict` that contains counts of occurrences for each word in some sample text: ```python words_frequency = { "the": 4, "a": 3, "dog": 1, "bone": 1, "wants": 1, ... } ``` Here is how we can iterate over the `dict`, accessing both the keys and values: ```python for word, count in word_frequency.items(): print(f"- {word} appears {count} time{'' if count == 1 else 's'}") ``` Using the [`items()`](https://docs.python.org/3/library/stdtypes.html#dict.items) method, we're able to access both _key_ and _value_ with the for loop as it iterates. Another approach is to loop directly on the `dict` which implicitly surfaces the _key_ for iteration. This can then be used to get the value from the `dict`: ```python for word in word_frequency: print(f"- {word}: {word_frequency[word]} ``` ================================================ FILE: python/keep-a-tally-with-collections-counter.md ================================================ # Keep A Tally With collections.Counter Python's `collections` module comes with a [`Counter`](https://docs.python.org/3/library/collections.html#collections.Counter) object which is a specialized dict subclass focussed on tallying counts of keys. > It is a collection where elements are stored as dictionary keys and their > counts are stored as dictionary values. Counts are allowed to be any integer > value including zero or negative counts. I used it recently while doing an exploratory implementation of a Byte-Pair Encoding (BPE): ```python from collections import Counter def get_pair_counts(token_ids: list[int]) -> Counter: """Count how often each adjacent pair appears""" counts = Counter() for i in range(len(token_ids) - 1): pair = (token_ids[i], token_ids[i + 1]) counts[pair] += 1 return counts ``` Here I'm able to count the number of occurrences of each pair of bytes from the input text. A tuple of `int` values is hashable, so they work great as keys for a `Counter`. The count value of any key will default to `0`. That makes it straightforward to increment from there as you iterating over occurrences. ```python >>> counts = Counter() >>> counts['hello'] 0 >>> count['hello'] += 1 >>> count['hello'] 1 ``` ================================================ FILE: python/load-a-file-into-the-python-repl.md ================================================ # Load A File Into The Python REPL I opened up a Python REPL to try some things out. ``` $ python3 >>> import math >>> math.floor(5/2) 2 ``` Now, I want to reference a Python file I've been working on so that I can manually test the behavior of what I'm building. To do this, I can import a file by its name in the same way that I would import any module. Then I can use that namespace for class and method references. Crucially, the file should exist in the same directory the REPL was started from. First, here is the file: ```python # bpe.py class BytePairEncoding: def text_to_bytes(text: str) -> list[int]: """Convert a string to a list of byte values (0-255)""" return list(text.encode("utf-8")) ``` Now to use it from the REPL: ``` $ python >>> import bpe >>> bpe.BytePairEncoding.text_to_bytes("Gimme some bytes!") [71, 105, 109, 109, 101, 32, 115, 111, 109, 101, 32, 98, 121, 116, 101, 115, 33] ``` ================================================ FILE: python/look-inside-pytest-tmp-path.md ================================================ # Look Inside Pytest tmp_path In [Isolate and Debug File Side-Effects with Pytest `tmp_path`](https://www.visualmode.dev/isolate-and-debug-file-side-effects-with-pytest-tmp-path), I wrote about how I use [`tmp_path`](https://docs.pytest.org/en/stable/reference/reference.html#std-fixture-tmp_path) in a Pytest fixture to test [my `py-vmt` CLI](https://github.com/jbranchaud/py-vmt). During testing of the CLI interface via [`click`'s testing utilities](https://click.palletsprojects.com/en/stable/testing/), `vmt` creates, modifies, and reads from files. Isolating that behavior with the `tmp_path` fixture is useful because it prevents individual test cases from conflicting with one another. Here is what the fixture looks like at the top of my test file: ```python # auto fixture for all test cases that monkeypatches the platform dirs to a tmp # path so that test side-effects don't persist between runs @pytest.fixture(autouse=True) def use_tmp_platform_dirs(tmp_path, monkeypatch): data_dir = tmp_path / "data" config_dir = tmp_path / "config" data_dir.mkdir() config_dir.mkdir() monkeypatch.setattr(CliContext, "get_data_dir", staticmethod(lambda: data_dir)) monkeypatch.setattr(CliContext, "get_config_dir", staticmethod(lambda: config_dir)) ``` The root of the temp directory is located at `tempfile.gettempdir()` and the directories from there are organized with this structure: ``` {temproot}/pytest-of-{user}/pytest-{num}/{testname}/ ``` So, in the case of `vmt`, I can find the `config` and `data` dirs for a specific test run here: ```bash ❯ ls /var/folders/zc/q6gnvbgx6kq77828jn38716r0000gn/T/pytest-of-lastword/pytest-2/test_start_status_stop_flow0 config data ``` ================================================ FILE: python/override-the-boolean-context-of-a-class.md ================================================ # Override The Boolean Context Of A Class Everything in Python has a truthiness that can be checked with `bool()`. An empty list (`[]`) is falsy. A non-empty list (`[1,2,3]`) is truthy. Similar with numbers: ```python >>> bool(0) False >>> bool(1) True ``` Any instance of an object is going to be truthy by default. If you want to control in what context an instance is considered truthy or falsy, you can override [`__bool__()`](https://docs.python.org/3/reference/datamodel.html#object.__bool__). If that's not implemented, but [`__len__()`](https://docs.python.org/3/reference/datamodel.html#object.__len__) is, then it will fallback to that. Let's look at a few example classes: ```python class CartZero: def __init__(self, items=[]): self.items = items or [] class CartBool: def __init__(self, items=[]): self.items = items or [] def __bool__(self): print("__bool__() override") return bool(self.items) class CartLen: def __init__(self, items=[]): self.items = items or [] def __len__(self): print("__len__() override") return len(self.items) class CartBoolAndLen: def __init__(self, items=[]): self.items = items or [] def __len__(self): print("__len__() override") return len(self.items) def __bool__(self): print("__bool__() override") return bool(self.items) cart1 = CartZero() cart2 = CartBool() cart3 = CartLen() cart4 = CartBoolAndLen() print("CartZero() -> %s" %(bool(cart1))) print('') print("CartBool() -> %s" %(bool(cart2))) print('') print("CartLen() -> %s" %(bool(cart3))) print('') print("CartBoolAndLen() -> %s" %(bool(cart4))) ``` An 'empty' `Cart` be default is truthy. However, we can override some combination of `__bool__()` or `__len__()` to give it a boolean context that goes `false` when "empty". ``` CartZero() -> True __bool__() override CartBool() -> False __len__() override CartLen() -> False __bool__() override CartBoolAndLen() -> False ``` ================================================ FILE: python/parse-relative-time-to-datetime-object.md ================================================ # Parse Relative Time To datetime Object I was looking for an out-of-the-box solution for parsing natural language, relative time strings (e.g. `'33 minutes ago'`) into valid `datetime` objects. The best library for this is [`dateparser`](https://dateparser.readthedocs.io/en/latest/). While it is as easy to use this as _import_ then _parse_: ```python >>> import dateparser >>> dateparser.parse('33 minutes ago') datetime.datetime(2026, 3, 7, 23, 19, 9, 17855) ``` There is more to it if we need to deal with timezones. In my use case, I wanted to my `datetime` object to be timezone-aware and I wanted to store it in `UTC`. As is, the above simple `datetime` object is not `tzaware`, meaning it doesn't have any `tzinfo` attached to it. ```python >>> dateparser.parse('33 minutes ago').tzinfo is not None False ``` We need to pass some additional settings during `parse`. ```python >>> settings = {'RETURN_AS_TIMEZONE_AWARE': True} >>> dateparser.parse('33 minutes ago', settings=settings) >>> _ datetime.datetime(2026, 3, 8, 9, 53, 36, 225099, tzinfo=zoneinfo.ZoneInfo(key='America/Chicago')) >>> settings['TO_TIMEZONE'] = 'UTC' >>> dateparser.parse('33 minutes ago', settings=settings) >>> _ datetime.datetime(2026, 3, 8, 14, 54, 47, 34041, tzinfo=) ``` The first step to getting a `datetime` object that is `tzaware` is to set `RETURN_AS_TIMEZONE_AWARE` to `True`. That picks up the locale setting of the system it is running on -- in my case, I'm in Chicago. I said I wanted to store this as UTC though. That means I need to pass an additional setting `TO_TIMEZONE` with a value of `'UTC'` which will translate the `datetime` from my local time to UTC -- notice the 5 hour difference from `9` to `14`. Storing `datetime` details like this with timezone info _as_ UTC is nice because it keeps everything consistent at the storage layer and then at the presentation layer I can always convert it right back to the local timezone with `astimezone`. ```python >>> _.astimezone() datetime.datetime(2026, 3, 8, 9, 54, 47, 34041, tzinfo=datetime.timezone(datetime.timedelta(days=-1, seconds=68400), 'CDT')) ``` See the [`datetime` docs](https://docs.python.org/3/library/datetime.html) for more details. ================================================ FILE: python/store-and-access-immutable-data-in-a-tuple.md ================================================ # Store And Access Immutable Data In A Tuple You can store heterogeneous data (of varying types) as a _tuple_ which is a light-weight immutable data structure. You can be explicit about the tuple by wrapping the items in parentheses: ```python >>> book = ('An Immense World', 'Ed Yong', 2022) ``` Though it is also possible to comma-separate the items and forego the parentheses. ```python >>> book2 = 'The Shining', 'Stephen King', 1977 >>> book2 ('The Shining', 'Stephen King', 1977) ``` Once we have our tuple, we can access any item from it positionally. We can also use _sequence unpacking_ to assign the values to a series of variables: ```python >>> book[0] 'An Immense World' >>> book[1] 'Ed Yong' >>> book[2] 2022 >>> title, author, publication_year = book >>> title 'An Immense World' >>> author 'Ed Yong' >>> publication_year 2022 ``` And, as promised, it is immutable (unlike lists): ```python >>> book[1] = 'Agatha Christie' Traceback (most recent call last): File "", line 1, in TypeError: 'tuple' object does not support item assignment ``` [source](https://docs.python.org/3/tutorial/datastructures.html#tuples-and-sequences) ================================================ FILE: python/test-a-function-with-pytest.md ================================================ # Test A Function With Pytest The [`pytest` framework](https://docs.pytest.org/en/latest/index.html) is a solid choice for unit testing your python project. Any file whose name is preceeded with `test_` that contains functions whose names are preceeded with `test_` will be processed and executed by the pytest test runner. ```python # test_taco.py def taco(day): return "Taco " + day def test_taco_tuesday(): assert taco("Tuesday") == "Taco Tuesday" # passes def test_taco_blank(): assert taco("") == "Taco" # fails, missing trailing space ``` Use `assert` statements to check that a comparison is `true`. If it isn't the `assert` statement will result in a test failure with some output about what went wrong. Ensure you have `pytest` installed and then run the following from your project directory: ```bash $ pytest #=> ... you'll see the test output below ``` ================================================ FILE: python/use-pipx-to-install-end-user-apps.md ================================================ # Use pipx To Install End User Apps The [`pipx`](https://pipx.pypa.io/stable/) tool is an installer for the python ecosystem. It differs from `pip` in that it is for installing end-user applications and it does so in isolated environments. You can install `pipx` with an OS-specific installer like Homebrew: ```bash $ brew install pipx ``` Ensure `pipx`-installed apps are on your path: ```bash $ pipx ensurepath ``` Then use `pipx` to install programs like [`cowsay`](https://pypi.org/project/cowsay/) or [`llm`](https://llm.datasette.io/en/stable/setup.html): ```bash $ pipx install llm $ which llm /Users/jbranchaud/.local/bin/llm $ llm --version llm, version 0.13.1 ``` ================================================ FILE: python/use-verbose-flag-to-get-more-diff.md ================================================ # Use Verbose Flag To Get More Diff Here is the output of running some `pytest` unit tests. A couple of the tests pass, which produces little output. But I get a big block of details for the one failing test. In this case the failure is an assertion between two lists that don't match. ```bash ❯ uv run pytest ========================================== test session starts ========================================== platform darwin -- Python 3.12.12, pytest-9.0.2, pluggy-1.6.0 rootdir: /Users/lastword/dev/misc/build-an-llm configfile: pyproject.toml collected 3 items tests/chapter_02/test_bpe_tokenizer.py .F. [100%] =============================================== FAILURES ================================================ _____________________________________ test_merge_with_byte_sequence _____________________________________ def test_merge_with_byte_sequence(): token_ids = [1, 2, 3, 4, 5, 2, 3, 1, 2, 3, 4, 1] merged_tokens = BPETokenizer._merge(token_ids, [2, 3, 4], 256) # assert merged_tokens == [1, 256, 5, 2, 3, 1, 256, 1] > assert merged_tokens == [1, 256, 5, 4, 5, 1, 256, 1] E assert [1, 256, 5, 2, 3, 1, ...] == [1, 256, 5, 4, 5, 1, ...] E E At index 3 diff: 2 != 4 E Use -v to get more diff tests/chapter_02/test_bpe_tokenizer.py:13: AssertionError ======================================== short test summary info ======================================== FAILED tests/chapter_02/test_bpe_tokenizer.py::test_merge_with_byte_sequence - assert [1, 256, 5, 2, 3, 1, ...] == [1, 256, 5, 4, 5, 1, ...] ====================================== 1 failed, 2 passed in 0.02s ====================================== ``` The lists are too long to fully display in the failure output. `pytest` is able to tell us two useful things though. First, it mentions that the first discrepancy in the lists is at index `3` where `2 != 4`. Second, it says `Use -v to get more diff`. Let's try rerunning the tests with `-v`. ```bash ❯ uv run pytest -v ========================================== test session starts ========================================== platform darwin -- Python 3.12.12, pytest-9.0.2, pluggy-1.6.0 -- /Users/lastword/dev/misc/build-an-llm/.venv/bin/python3 cachedir: .pytest_cache rootdir: /Users/lastword/dev/misc/build-an-llm configfile: pyproject.toml collected 3 items tests/chapter_02/test_bpe_tokenizer.py::test_merge_with_byte_pair PASSED [ 33%] tests/chapter_02/test_bpe_tokenizer.py::test_merge_with_byte_sequence FAILED [ 66%] tests/chapter_02/test_bpe_tokenizer.py::test_subsequence_at_index PASSED [100%] =============================================== FAILURES ================================================ _____________________________________ test_merge_with_byte_sequence _____________________________________ def test_merge_with_byte_sequence(): token_ids = [1, 2, 3, 4, 5, 2, 3, 1, 2, 3, 4, 1] merged_tokens = BPETokenizer._merge(token_ids, [2, 3, 4], 256) # assert merged_tokens == [1, 256, 5, 2, 3, 1, 256, 1] > assert merged_tokens == [1, 256, 5, 4, 5, 1, 256, 1] E AssertionError: assert [1, 256, 5, 2, 3, 1, ...] == [1, 256, 5, 4, 5, 1, ...] E E At index 3 diff: 2 != 4 E E Full diff: E [ E 1, E 256,... E E ...Full output truncated (13 lines hidden), use '-vv' to show tests/chapter_02/test_bpe_tokenizer.py:13: AssertionError ======================================== short test summary info ======================================== FAILED tests/chapter_02/test_bpe_tokenizer.py::test_merge_with_byte_sequence - AssertionError: assert [1, 256, 5, 2, 3, 1, ...] == [1, 256, 5, 4, 5, 1, ...] ====================================== 1 failed, 2 passed in 0.02s ====================================== ``` That was sort of a tease because it starts to display a "Full diff", but that gets quickly truncated. `pytest` then tells us that we can `use '-vv' to show` the full diff. ```bash ❯ uv run pytest -vv ========================================== test session starts ========================================== platform darwin -- Python 3.12.12, pytest-9.0.2, pluggy-1.6.0 -- /Users/lastword/dev/misc/build-an-llm/.venv/bin/python3 cachedir: .pytest_cache rootdir: /Users/lastword/dev/misc/build-an-llm configfile: pyproject.toml collected 3 items tests/chapter_02/test_bpe_tokenizer.py::test_merge_with_byte_pair PASSED [ 33%] tests/chapter_02/test_bpe_tokenizer.py::test_merge_with_byte_sequence FAILED [ 66%] tests/chapter_02/test_bpe_tokenizer.py::test_subsequence_at_index PASSED [100%] =============================================== FAILURES ================================================ _____________________________________ test_merge_with_byte_sequence _____________________________________ def test_merge_with_byte_sequence(): token_ids = [1, 2, 3, 4, 5, 2, 3, 1, 2, 3, 4, 1] merged_tokens = BPETokenizer._merge(token_ids, [2, 3, 4], 256) # assert merged_tokens == [1, 256, 5, 2, 3, 1, 256, 1] > assert merged_tokens == [1, 256, 5, 4, 5, 1, 256, 1] E assert [1, 256, 5, 2, 3, 1, 256, 1] == [1, 256, 5, 4, 5, 1, 256, 1] E E At index 3 diff: 2 != 4 E E Full diff: E [ E 1, E 256, E 5, E - 4, E ? ^ E + 2, E ? ^ E - 5, E ? ^ E + 3, E ? ^ E 1, E 256, E 1, E ] tests/chapter_02/test_bpe_tokenizer.py:13: AssertionError ======================================== short test summary info ======================================== FAILED tests/chapter_02/test_bpe_tokenizer.py::test_merge_with_byte_sequence - assert [1, 256, 5, 2, 3, 1, 256, 1] == [1, 256, 5, 4, 5, 1, 256, 1] At index 3 diff: 2 != 4 Full diff: [ 1, 256, 5, - 4, ? ^ + 2, ? ^ - 5, ? ^ + 3, ? ^ 1, 256, 1, ] ====================================== 1 failed, 2 passed in 0.02s ====================================== ``` This is a lot more output to look at. What we can perhaps see more clearly now is that the lists match up until there is a mismatch between `2` and `4` at the third index. And then right after that is another mismatch between `3` and `5`. This kind of output can only scale so much, so use it when it works and when the diff view starts to fall short, rework the assertions to get more readable and actionable test output. ================================================ FILE: rails/access-secrets-in-a-rails-5-2-app.md ================================================ # Access Secrets In A Rails 5.2 App For a long time the access chain for getting at secrets in your Rails app stayed the same. For instance, getting at the `secret_key_base` value looked something like this: ```ruby Rails.application.secrets.secret_key_base ``` In the world of Rails 5.2, secrets are no longer secrets. They are now credentials. This means they are under the `credentials` key instead of the `secrets` key. Here is how you can access `secret_key_base` now: ```ruby Rails.application.credentials.secret_key_base ``` [source](https://www.engineyard.com/blog/rails-encrypted-credentials-on-rails-5.2) ================================================ FILE: rails/active-record-query-for-this-or-that.md ================================================ # ActiveRecord Query For This Or That When including multiple `where` clauses on a query, we are adding more specificity to the resulting `ActiveRecord` relation -- it's like saying we want records that match this _and_ that. But what about when we want to find records that match this _or_ that? This is supported by `ActiveRecord` through the `or` query method. Let's say we want all books that are either unpublished _or_ are published in 2019. ```ruby > Book.where(status: 'unpublished').or(Book.where(publication_year: 2019)) => # ``` This will generate SQL that includes a `where` clause like the following: ```sql where (books.status = 'unpublished' or books.publication_year = 2019) ``` See the [docs](https://api.rubyonrails.org/classes/ActiveRecord/QueryMethods.html#method-i-or) for more details. ================================================ FILE: rails/add-a-check-constraint-to-a-table.md ================================================ # Add A Check Constraint To A Table PostgreSQL allows you to enforce all kinds of rules about the value of a column or the relationship between two columns. These rules are defined with [_check constraints_](https://www.postgresql.org/docs/current/ddl-constraints.html#DDL-CONSTRAINTS-CHECK-CONSTRAINTS). ActiveRecord's migration DSL does not provide a way for adding check constraints directly. They can be added by executing a SQL statement in a migration. ```ruby class EnsurePageCountIsPositive < ActiveRecord::Migration[5.2] def up execute <<-SQL alter table books add constraint ensure_page_count_is_positive check (page_count > 0); SQL end def down execute <<-SQL alter table books drop constraint ensure_page_count_is_positive; SQL end ``` This check constraint ensures that, anytime you add or update a row in the book column, the value of `page_count` column is always greater than `0`. This is a nice thing to enforce because it wouldn't make much sense for a book to have, say, `-10` pages. Note: these constraints will not appear in your `db/schema.rb` file. If you want to see what check constraints have been defined across your tables, you can crack open `psql` to investigate. ================================================ FILE: rails/add-a-database-index-if-it-does-not-already-exist.md ================================================ # Add A Database Index If It Does Not Already Exist Sometimes you aren't sure if an index might already exist in one of the environments where a migration is going to run. But you still need to add the index elsewhere. One way of handling that is to add an index with the `if_not_exists` directive. ```ruby class AddIndexToEventsCreatedAt < ActiveRecord::Migration[6.1] def change add_index :events, :created_at, if_not_exists: true end end ``` `ActiveRecord` will translate this directive into the resulting SQL statement like so: ```sql create index if not exists index_events_on_created_at on events ... ; ``` This way the index will be created in a database where it doesn't already exist and otherwise the statement will short-circuit rather than erroring when one does exist. A couple notes: 1) From the PostgreSQL manual: > there is no guarantee that the existing index is anything like the one that > would have been created. 2) The `if_not_exists` option also works with `create_table`. [source](https://edgeapi.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/SchemaStatements.html) ================================================ FILE: rails/add-a-foreign-key-reference-to-a-table.md ================================================ # Add A Foreign Key Reference To A Table Foreign keys are a great way to maintain referential integrity within our data. We can add reference columns with foreign key constraints using the Rails migration DSL. Here is how we include one as part of creating a table: ```ruby def up create_table :books do |t| # ... other columns t.references :author, index: true, foreign_key: true end end ``` This will add a column, `author_id`, to the `books` table that references the `authors` table. It will have both a foreign key constraint and an index applied to it. Here is how we do the same for an existing table: ```ruby def up add_reference :books, :author, index: true, foreign_key: true end ``` As of Rails 5, this is a bit verbose as `index: true` happens by default. Though I'm always in favor of explicitness. If for whatever reason you don't want an index, you will have to specify `index: false`. ================================================ FILE: rails/add-a-generated-column-to-a-postgresql-table.md ================================================ # Add A Generated Column To A PostgreSQL Table As of Rails 7, ActiveRecord supports generated columns for app's backed by a PostgreSQL database. This is achieved with a `virtual` column. ```ruby class CreateTags < ActiveRecord::Migration[8.0] def change create_table :tags, id: :bigint do |t| t.string :value t.virtual :normalized_value, type: :text, as: "lower(value)", stored: true t.timestamps end end end ``` With a table like this, any time we add a record with a `value`, PostgreSQL computes and stores the `normalized_value` column based on that. [source](https://blog.saeloun.com/2022/01/25/rails-7-postgres-support-for-generated-columns/) ================================================ FILE: rails/add-a-reference-column-with-an-index.md ================================================ # Add A Reference Column With An Index Though I prefer to always back my reference columns with a [foreign key](add-a-foreign-key-reference-to-a-table.md), sometimes you may just want to add the reference column on its own. Though this could be done manually with the `add_column` directive, you can be more explicit with `add_reference` -- which allows you to specify whether or not an index is to be added. ```ruby def up add_reference :books, :author, index: true end ``` This will add `authors_id` and an index to the `books` table. You can additionally specify the type of the column. This is handy if you are using `uuid`s for all your primary keys. ```ruby def up add_reference :books, :author, type: :uuid, index: true end ``` [source](https://nandovieira.com/using-uuid-with-postgresql-and-activerecord) ================================================ FILE: rails/add-activerecord-error-not-tied-to-any-attribute.md ================================================ # Add ActiveRecord Error Not Tied To Any Attribute Often the [errors on an ActiveRecord object](https://api.rubyonrails.org/v6.1.3.2/classes/ActiveModel/Errors.html) are tied to a specific attribute of that object. For instance, when this validation is violated ```ruby validates :name, presence: true ``` Then the error will be tied to `:name`. With the [`ActiveModel::Errors#add`](https://api.rubyonrails.org/v6.1.3.2/classes/ActiveModel/Errors.html#method-i-add) method, we can write custom validation logic that ties an error to a specific attribute. ```ruby validate :quantity_for_bulk_purchase def quantity_for_bulk_purchase return if purchase_type != :bulk if quantity < 12 errors.add(:quantity, "must be greater than 12 for bulk purchases") end end ``` Errors don't have to be tied to specific attribute. They can be tied to the object as a whole. This can be better for validations, like the one above, that involve multiple attributes. ```ruby validate :quantity_for_bulk_purchase def quantity_for_bulk_purchase return if purchase_type != :bulk if quantity < 12 errors.add(:base, "Quantity must be greater than 12 for bulk purchases") end end ``` By using the `:base` symbol, we are ascribing this error to the object as a whole. ``` > my_object.errors #=> #[{:error=>"Quantity must be greater than 12 for bulk purchases"}]}, @messages={:base=>["Quantity must be greater than 12 for bulk purchases"]}> > my_object.errors.full_messages #=> ["Quantity must be greater than 12 for bulk purchases"] ``` ================================================ FILE: rails/add-color-to-the-irb-console-prompt.md ================================================ # Add Color To The IRB Console Prompt IRB has a little-known [`Color` module](https://docs.ruby-lang.org/en/3.2/IRB/Color.html) with some helpers for adding a splash of color to the IRB prompt. I like to clearly differentiate the environment I'm in when connecting to the `rails console`, so I have a customize the prompt to display and colorize the current environment. I can wrap any string in ANSI escape codes that instruct the terminal to style the text with color. For instance, here is how I can style the word `DEV` to be inverted against a blue background. ```ruby IRB::Color.colorize("DEV", [:BLUE, :BOLD, :REVERSE]) ``` which will clearly stand out from `PROD` against a red background: ```ruby IRB::Color.colorize("PROD", [:RED, :BOLD, :REVERSE]) ``` Here is a full example of customizing the prompt from the `config/application.rb` file. ```ruby module MyApp class Application < Rails::Application # ... console do # Get the application module name and convert to kebab-case app_name = Rails.application.class.module_parent.name kebab_name = app_name.underscore.dasherize # Environment color coding env_colors = { "development" => IRB::Color.colorize("DEV", [:BLUE, :BOLD, :REVERSE]), "production" => IRB::Color.colorize("PROD", [:RED, :BOLD, :REVERSE]), "test" => IRB::Color.colorize("TEST", [:YELLOW, :BOLD, :REVERSE]), } colored_env = "(#{env_colors[Rails.env]})" # Docs: https://docs.ruby-lang.org/en/3.2/IRB.html#module-IRB-label-Customizing+the+IRB+Prompt IRB.conf[:PROMPT][:RAILS_APP] = { PROMPT_I: "#{kebab_name}#{colored_env}> ", PROMPT_N: "#{kebab_name}#{colored_env}* ", PROMPT_S: "#{kebab_name}#{colored_env}% ", PROMPT_C: "#{kebab_name}#{colored_env}? ", RETURN: "=> %s\n" } # Set it as the current prompt IRB.conf[:PROMPT_MODE] = :RAILS_APP end end end ``` The Ruby docs have more about [IRB Prompt Customization](https://docs.ruby-lang.org/en/3.2/IRB.html#module-IRB-label-Customizing+the+IRB+Prompt). ================================================ FILE: rails/add-react-with-webpacker-to-a-new-rails-app.md ================================================ # Add React With Webpacker To A New Rails App [Webpacker](https://github.com/rails/webpacker) makes it easy to manage app-like JavaScript in the context of a Rails app. React is a great candidate for this kind of webpack-powered JavaScript processing pipeline. To set up a new Rails project with Webpack and React wired up, add the `--webpack=react` flag: ```bash $ rails new rails-react-app --webpack=react ``` As part of the generated app, you will get a `app/javascript/packs` directory with a `hello_react.jsx` file that has a really basic React component. [source](https://github.com/rails/webpacker#react) ================================================ FILE: rails/add-timestamptz-columns-with-the-migration-dsl.md ================================================ # Add timestamptz Columns With The Migration DSL The Rails migration DSL comes with the [`t.timestamps`](https://edgeapi.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/TableDefinition.html#method-i-timestamps) method. This allows you to add the `created_at` and `updated_at` timestamp columns that are standard for most models in Rails apps. ```ruby create_table :posts do |t| t.string :title, null: false # ... t.timestamps end ``` With a PostgreSQL database, this will result in a `posts` table that has `created_at` and `updated_at` columns that are of type `timestamp(6) without time zone`. I'd prefer to use timestamp columns that include a time zone offset. PostgreSQL supports this with its [`timestamptz` (`timestamp with time zone`)](https://www.postgresql.org/docs/current/datatype-datetime.html) data type. We can tell the Rails DSL to generate this type of column by abondoning the `t.timestamps` method and instead creating custom columns with [`t.column`](https://edgeapi.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/TableDefinition.html#method-i-column). ```ruby create_table :posts do |t| t.string :title, null: false # ... t.column :created_at, :timestamptz, null: false t.column :updated_at, :timestamptz, null: false end ``` It is a little less convenient than the `t.timestamps` helper, but it is nice to know we can have a little more control over the data type. ================================================ FILE: rails/adjust-the-production-log-level.md ================================================ # Adjust The Production Log Level A Rails app by default takes on the `debug` log level. This is great for development because it spits out a lot of information as you build and debug. That's going to typically be a bit too noisy for a production environment though. That is why Rails ships with the `config/environments/production.rb` file configured to the `info` log level. ```ruby # config/environments/production.rb Rails.application.configure do # ... # "info" includes generic and useful information about system operation, but avoids logging too much # information to avoid inadvertent exposure of personally identifiable information (PII). If you # want to log everything, set the level to "debug". config.log_level = ENV.fetch("RAILS_LOG_LEVEL", "info") # ... end ``` Sometimes, like when we're trying to track down some buggy behavior, we may want to switch Rails from one log level to another. That's why it is configured by the `RAILS_LOG_LEVEL` env var and otherwise falls back to `info`. To, for example, switch production over to the `debug` log level, we'd first change the `RAILS_LOG_LEVEL` env var to `debug`. Then we'd need to make sure our Rails app is restarted so that the config change is picked up. Heroku's `heroku config:set` will do that automatically. Depending on your setup, you may need to manually restart your web server (e.g. Puma). ================================================ FILE: rails/advance-the-date.md ================================================ # Advance The Date In Rails land, you can advance a date forward and backward with the `#advance` method: ```ruby > Date.today => Wed, 31 Aug 2016 > Date.today.advance(days: 1) => Thu, 01 Sep 2016 > Date.today.advance(months: 1) => Fri, 30 Sep 2016 > Date.today.advance(months: -2) => Thu, 30 Jun 2016 ``` h/t Dillon Hafer ================================================ FILE: rails/all-or-nothing-database-transactions.md ================================================ # All or Nothing Database Transactions When you are updating multiple records in an *all or nothing* scenario, you can use [Active Record Transactions](http://api.rubyonrails.org/classes/ActiveRecord/Transactions/ClassMethods.html) to ensure that either everything is updated or none of it is updated. For instance, if you are transferring *internet points* from one user's account to another user's account, you need to be sure that the transfer balances out. If updating one user is successful, but updating the other fails, then there will be a discrepancy in the data. A transaction will ensure that when any part of the update fails the entire transaction is rolled back (at the database level). ```ruby User.transaction do user1.internet_points += 20 user2.internet_points -= 20 user1.save! user2.save! end ``` ================================================ FILE: rails/allow-associations-to-be-optional.md ================================================ # Allow Associations To Be Optional As of Rails 5, whenever a `belongs_to` association is declared in an `ActiveRecord` model, it is assumed to be a required association. ```ruby class Book < ApplicationRecord belongs_to :author end ``` If we were to create a `Book` instance without an `Author`, then we would get an error – `Validation Failed: Author is missing`. We could either make sure to always create books with authors or, if it makes sense for our data model, we could treat the author as optional. The most explicit and precise way to make a relation optional is to declare it as such in the `belongs_to` directive. ```ruby class Book < ApplicationRecord belongs_to :author, optional: true end ``` Another approach, though I don't recommend it unless you have a strong reason, is to globally make associations optional. You can do this by adding the following line to your `config/application.rb` file. ```ruby config.active_record.belongs_to_required_by_default = false ``` [source](https://www.bigbinary.com/blog/rails-5-makes-belong-to-association-required-by-default) ================================================ FILE: rails/allow-list-params-anywhere-with-strong-params.md ================================================ # Allow List Params Anywhere With Strong Params The intended use of [`StrongParams`](https://api.rubyonrails.org/classes/ActionController/StrongParameters.html) is to prevent unintended params from getting through a controller action during mass assignment. This can be put to use other places in your Rails app, such as a service object, where mass assignment is used to update records. ```ruby class BookTitleUpdater ALLOW_LIST = [:title].freeze def self.run(data) params = ActionController::Parameters.new(data).permit(*ALLOW_LIST) Book.find(data[:id]).update!(params) end end ``` This helps prevent other values from getting inadvertently updated on the `book` record. ```ruby > ALLOW_LIST = [:title] > data = { title: "Legacy Code", author_id: 22 } > params = ActionController::Parameters.new(data).permit(*ALLOW_LIST) > params.to_h #=> { title: "Legacy Code" } ``` The `author_id` value is ignored and won't be passed to the `#update` call. ================================================ FILE: rails/alphabetize-schema-columns-to-keep-them-consistent.md ================================================ # Alphabetize Schema Columns To Keep Them Consistent When working on a Rails project with a team, there can be lots of unnecessary churn in the `db/schema.rb` file. While there are a couple things that can cause this churn, the main one is reordering of columns during local migration. The [`strong_migrations` gem](https://github.com/ankane/strong_migrations) provides [a handy rake task to apply alphabetical ordering to columns names](https://github.com/ankane/strong_migrations#schema-sanity). This keeps them in a consistent order which reduces churn. Assuming you have the `strong_migrations` gem included in your app, add the following line to the end of your `Rakefile`. ```ruby task "db:schema:dump": "strong_migrations:alphabetize_columns" ``` This sets `strong_migrations:alphabetize_columns` as a prerequisite task to `db:schema:dump`. Whenever `db:schema:dump` gets run, the alphabetization task will get run first. This ensures the resulting `db/schema.rb` file always has column names in a consistent order. The origin of this idea is Paul Gross's blog post [Alphabetize schema.rb Columns](https://www.pgrs.net/2008/03/12/alphabetize-schema-rb-columns/). ================================================ FILE: rails/alter-the-rails-setup-script.md ================================================ # Alter The Rails Setup Script When you generate a new Rails app, a set of scripts are put in the `bin/` folder of your new app. These _bin scripts_ are ruby scripts that you can use to run `rails` commands, `rake` commands, as well as `setup` your rails project. These scripts can be modified like you'd modify any other ruby code. In fact, the `setup` scripts encourages you to modify it by providing an example of an additional setup step you can add. ```bash #!/usr/bin/env ruby require "fileutils" # ... FileUtils.chdir APP_ROOT do # This script is a way to set up or update your development environment automatically. # This script is idempotent, so that you can run it at any time and get an expectable outcome. # Add necessary setup steps to this file. puts "\n== Installing dependencies ==" system! "gem install bundler --conservative" system("bundle check") || system!("bundle install") # puts "\n== Copying sample files ==" # unless File.exist?("config/database.yml") # FileUtils.cp "config/database.yml.sample", "config/database.yml" # end # ... end ``` There are several steps built in, but it provides an example of how you can copy a sample YAML file to be the actual version of that YAML file. ================================================ FILE: rails/apply-basic-html-formatting-to-block-of-text.md ================================================ # Apply Basic HTML Formatting To Block Of Text My Rails app has a form that allows a user to enter in free-form text. I enter in a couple paragraphs and save the record. It is rendered on a show page with a couple lines of ERB like so: ```ruby
    <%= @record.notes %>
    ``` When I view the erb-displayed version of that record's text, all those carefully spaced paragraphs are clumped together. That is because those newline (`\n` and `\n\n`) characters while understood to be whitespace do not have formatting implications in the browser like a combination of HTML tags and CSS do. I can apply some basic formatting with [the aptly named `simple_format` method available as an `ActionView` helper](https://api.rubyonrails.org/classes/ActionView/Helpers/TextHelper.html#method-i-simple_format). ```ruby
    <%= simple_format(@record.notes) %>
    ``` This turns single `\n` characters into a `
    ` tag and double `\n\n` cause the surrounding paragraphs to be wrapped in `

    ` tags. That simple formatting combined with my existing TailwindCSS styles makes the formatting of my text immediately look much better. ================================================ FILE: rails/assert-two-arrays-have-the-same-items-with-rspec.md ================================================ # Assert Two Arrays Have The Same Items With RSpec Methods that return arrays of values with inconsistent orderings can be annoying to test with the `#eq` matcher. To keep your test from fickering, you'd have to ensure the comparison is the same every time. ```ruby it "has the correct values" do expect(fetch_colors(params).sort).to eq(["blue", "green", "yellow"]) end ``` It'd be better if we could keep our test focused and simple. If sort order isn't something we care about, then it shouldn't be part of our test. RSpec has a matcher for this kind of scenario -- [`#match_array`](https://www.rubydoc.info/github/rspec/rspec-expectations/RSpec%2FMatchers:match_array). ```ruby it "has the correct values" do expect(fetch_colors(params)).to match_array(["blue", "green", "yellow"]) end ``` This allows us to ensure that each side of the comparison has the same set values, irrespective of ordering. ================================================ FILE: rails/attach-a-file-with-capybara.md ================================================ # Attach A File With Capybara There are two ways to attach a file with [Capybara](https://github.com/jnicklas/capybara). The more conventional way is with the [`attach_file`](http://www.rubydoc.info/github/jnicklas/capybara/Capybara%2FNode%2FActions%3Aattach_file) method. Assuming there is a form with a file input similar to the following: ```html ``` ```ruby attach_file('data-file', 'path/to/file.csv') ``` The first argument to `attach_file` is a locator which refers to the `name` attribute on the `input` tag. If for some reason there is no `name` attribute on the `input` tag, the file can be attached with a standard `find` and `set`. ```ruby find('form input[type="file"]').set('path/to/file.csv') ``` ================================================ FILE: rails/attribute-getter-without-the-recursion.md ================================================ # Attribute Getter without the Recursion You may find yourself adding a custom *getter* method for one of the attributes in a Rails model. It might look something like this: ```ruby def name name || account.name end ``` This method will fall on its face as it quickly expands the stack recursively calling itself. Instead, you can tell `ActiveRecord` that you want the value of that attribute without invoking the getter Instead, we want to get the value of the attribute without invoking the model's getter. `ActiveRecord` allows us to do this with the [`read_attribute`](http://api.rubyonrails.org/classes/ActiveRecord/AttributeMethods/Read.html#method-i-read_attribute) method. Check it out: ```ruby def name read_attribute(:name) || account.name end ``` [source](http://stackoverflow.com/questions/21835116/overwrite-getter-activerecord-model-rails) ================================================ FILE: rails/attribute-was.md ================================================ # Attribute Was When modifying the attributes of an `ActiveRecord` object, you may want to know what values the modified attributes used to have. `ActiveRecord` gets some handy methods from the [`ActiveModel::Dirty`](http://api.rubyonrails.org/classes/ActiveModel/Dirty.html) module that allow you to check these values out even if the object's attributes were changed before you received it (though you are out of luck once it has been saved). Just add `_was` onto the end of the attribute in question. ```ruby >> pokemon.name => "Charizard" >> pokemon.name = "Squirtle" => "Squirtle" >> pokemon.name => "Squirtle" >> pokemon.name_was => "Charizard" >> pokemon.save ... => true >> pokemon.name_was == pokemon.name => true ``` [source](http://api.rubyonrails.org/classes/ActiveModel/Dirty.html) ================================================ FILE: rails/autosave-false-on-activerecord-associations.md ================================================ # Autosave False On ActiveRecord Associations A relationship between two ActiveRecord models can be established with a `has_one` or `has_many` association. This relationship has some implications. By default, saving a record will also save the associated records that have since been built. Consider this example of users that have many posts (`has_many posts`). ```ruby > u = User.first #=> # > u.posts #=> [] > u.posts.build(title: "Some Title", content: "This is a post") #=> # > u.save #=> true > u.posts(reload: true) #=> [#] ``` When the user is saved, the associated post that was built for that user also gets saved to the database. If the association is instead defined with the `autosave` option set to false, then saving a record will not cause associated records to also be saved. The associated records will need to be saved explicitly. Consider the same example from above, but with `has_many posts, autosave: false`. ```ruby > u = User.first #=> # > u.posts #=> [] > u.posts.build(title: "Some Title", content: "This is a post") #=> # > u.save #=> true > u.posts(reload: true) #=> [] ``` The post wasn't saved with the user and it wasn't saved explicitly, so it isn't persisted to the database. ================================================ FILE: rails/bind-parameters-to-activerecord-sql-query.md ================================================ # Bind Parameters To ActiveRecord SQL Query Many of the connection query methods that come with `ActiveRecord` accept an optional `binds` parameter. This can be used to safely inject parameters into the query. Here's a SQL query we could use with one of these methods: ```ruby sql = <<-SQL select coalesce(places.latitude, 41.8781) latitude, coalesce(places.longitude, -87.6298) longitude from places join appointments on places.id = apointments.places_id where appointments.id = $1 and status = $2 SQL ``` Notice the `$1` and `$2`, those are what will be bound to the two parameters included as `binds`. ```ruby connection = ActiveRecord::Base.connection binds = [[nil, appt_id], [nil, input_status]] coords = connection.select_one(sql, nil, binds) coords #=> { "latitude": 41.8781, "longitude": -87.6298 } ``` Notice the `binds` is an array of tuples. It's the second value in each tuple that gets bound the corresponding binding indicator in the sql. The syntax is a bit awkward since it is a lower-level API, however once you know it, you can manage. ================================================ FILE: rails/build-a-hash-of-model-attributes.md ================================================ # Build A Hash Of Model Attributes Have you ever found yourself creating an `ActiveRecord` object with [FactoryBot](https://github.com/thoughtbot/factory_bot) with the sole purpose of turning it into a hash of attributes? ```ruby > FactoryBot.build(:book).attributes { "id"=>nil, "title"=>"Fledgling", "genre"=>"fiction" } ``` FactoryBot has a built-in method for doing this: ```ruby > FactoryBot.attributes_for(:book) { title: "Fledgling", genre: "fiction" } ``` It also accepts any traits for that factory: ```ruby > FactoryBot.attributes_for(:book, :published) { title: "Fledgling", genre: "fiction", publication_year: 2005, page_count: 362 } ``` This is a handy way of build a base set of attributes when testing an API endpoint. [source](https://devhints.io/factory_bot) ================================================ FILE: rails/capture-development-emails-with-mailhog.md ================================================ # Capture Development Emails With Mailhog My preferred way to capture and view emails being sent by a Rails app in development is to use [MailHog](https://github.com/mailhog/MailHog). It runs a local SMTP server at port `1025` and a barebones email client at port `8025`. The `mailhog` utility can be installed with `brew`: ```bash $ brew install mailhog ``` The development `smtp` settings are configured in `config/environments/development.rb`: ```ruby config.action_mailer.delivery_method = :smtp config.action_mailer.smtp_settings = { address: 'localhost', port: 1025, } ``` Then start running `mailhog` with its default settings: ```bash $ mailhog ``` All outgoing email from the development server will be captured and viewable in both `html` and `text` form at `localhost:8025`. ================================================ FILE: rails/capybara-page-status-code.md ================================================ # Capybara Page Status Code To quickly determine if a page is rendering as expected or not, you can check the status code of the page. If your page is rendering successfully, you'll see something like this: ```ruby > page.status_code # => 200 ``` If some sort of application authorization logic is causing the page to not render as normal, you may see something like this: ```ruby > page.status_code # => 403 ``` ================================================ FILE: rails/cast-common-boolean-like-values-to-booleans.md ================================================ # Cast Common Boolean-Like Values To Booleans Sometimes you have to deal with values that are supposed to represent booleans, but they aren't actually boolean values (i.e. `"t"` instead of `true`). Rail's `ActiveModel` has a helper for casting these common boolean-like values to actual booleans. ```ruby > ActiveModel::Type::Boolean.new.cast('f') => false > ActiveModel::Type::Boolean.new.cast('t') => true > ActiveModel::Type::Boolean.new.cast('true') => true > ActiveModel::Type::Boolean.new.cast('FALSE') => false > ActiveModel::Type::Boolean.new.cast(0) => false > ActiveModel::Type::Boolean.new.cast(1) => true > ActiveModel::Type::Boolean.new.cast(true) => true > ActiveModel::Type::Boolean.new.cast(false) => false ``` This cast method gives you a handy way to handle all thsoe different cases. This is available as of Rails 5+. ================================================ FILE: rails/change-the-nullability-of-a-column.md ================================================ # Change The Nullability Of A Column Do you have an existing table with a column that is exactly as you want it except that it needs to be changed to either `null: false` or `null: true`? One option is to use ActiveRecord's `change_column_null` method in your migration. For example to change a nullable column to `null: false`, you'll want a migration like the following: ```ruby def change change_column_null :posts, :title, false end ``` Note, if you have existing records with `null` values in the `title` column, then you'll need to deal with those before migrating. If you want to make an existing column nullable, change that `false` to `true`: ```ruby def change change_column_null :posts, :title, true end ``` ================================================ FILE: rails/change-the-time-zone-offset-of-a-datetime-object.md ================================================ # Change The Time Zone Offset Of A DateTime Object Let's say you have a timestamp string that you parse with [`DateTime.parse`](https://ruby-doc.org/stdlib-2.6.1/libdoc/date/rdoc/DateTime.html#method-c-parse). ```ruby > DateTime.parse('2021-02-23T11:59:11') #=> Tue, 23 Feb 2021 11:59:11 +0000 ``` Without the specification of a time zone offset in the timestamp string, it will be parsed as UTC. If you want to change it to another time zone, you can alter the `offset` option of the `DateTime` object. Rails provides the [`#change`](https://api.rubyonrails.org/classes/DateTime.html#method-i-change) method for doing this. ```ruby > DateTime.parse('2021-02-23T11:59:11').change(offset: '-600') #=> Tue, 23 Feb 2021 11:59:11 -0600 ``` By changing the `offset` to `-600`, the `DateTime` now represents a time in Central Time. [source](https://stackoverflow.com/a/47861810/535590) ================================================ FILE: rails/check-how-database-is-configured.md ================================================ # Check How Database Is Configured While making some adjustments to the database connection string (`DATABASE_URL`) for a pre-production Rails environment, we wanted to check that configuration options like `sslmode` were picked up. From a `rails console` session I can check the live database configuration like so: ```ruby > ActiveRecord::Base.connection_db_config.configuration_hash => { adapter: "postgresql", encoding: "unicode", pool: 5, database: "my_app_development" } ``` I can look at the [`configuration_hash`](https://api.rubyonrails.org/classes/ActiveRecord/DatabaseConfigurations/HashConfig.html#attribute-i-configuration_hash) from `rails console` of my pre-prod environment to see more configuration settings: ```ruby > ActiveRecord::Base.connection_db_config.configuration_hash => { adapter: "postgresql", encoding: "unicode", pool: 5, username: "app_user", password: "super_s3cr3t", port: 15432, database: "pre_prod_database", host: "some-host-123.ondigitalocean.com", sslmode: "verify-full" } ``` Since I was specifically looking for the `sslmode` value, I can access that directly: ```ruby > ActiveRecord::Base.connection_db_config.configuration_hash[:sslmode] => "verify-full" ``` ================================================ FILE: rails/check-if-activerecord-update-fails.md ================================================ # Check If ActiveRecord Update Fails There are two ways to update an `ActiveRecord` instance (not to mention [`assign_attributes`](https://api.rubyonrails.org/classes/ActiveModel/AttributeAssignment.html), [`update_attribute`](https://api.rubyonrails.org/classes/ActiveRecord/Persistence.html#method-i-update_attribute), etc.). You can call [`update`](https://api.rubyonrails.org/classes/ActiveRecord/Persistence.html#method-i-update) and [`update!`](https://api.rubyonrails.org/classes/ActiveRecord/Persistence.html#method-i-update-21). If the updates would make the record invalid based on the model's validations, then the update will fail. You can tell if `update` failed because it will return `false` (instead of `true`). ```ruby unless book.update(book_params) log_book_update_failed(id: book.id) end ``` The `update!` version will raise an `ActiveRecord::ActiveRecordError` exception if the update fails. ```ruby begin book.update!(book_params) rescue ActiveRecord::ActiveRecordError log_book_update_failed(id: book.id) end ``` ================================================ FILE: rails/check-if-any-records-have-a-null-value.md ================================================ # Check If Any Records Have A Null Value I like to add missing `not null` constraints where appropriate when they are missing. The [`change_column_null` method](change-the-nullability-of-a-column.md) is the Rails DSL way of doing this. But first, you need to be sure that all databases this will be running against don't have any `null` values already present for any records. If there are `null` values present, then the migration will fail. One way of doing that check is with some SQL and the [`select_values`](https://api.rubyonrails.org/v6.1.0/classes/ActiveRecord/ConnectionAdapters/DatabaseStatements.html#method-i-select_values) method. ```ruby ActiveRecord::Base .connection .select_values("select id from projects where user_id is null") .any? ``` This bit of SQL asks for the `id`s of any records in the `projects` table where a specific column (`user_id`) is explicitly `null`. If this returns `false`, then you are good to migrate. If this returns `true`, then you'll have to do some data massaging first. ================================================ FILE: rails/check-specific-attributes-on-activerecord-array.md ================================================ # Check Specific Attributes On ActiveRecord Array If you're writing a test against a method that returns a collection of ActiveRecord results, it can be tedious to check specific values for each. There are certainly an endless number of ways to approach those assertions. You can keep your assertion fairly concise by using nested RSpec matchers. There are two in particular that can help with this kind of check: ```ruby books = get_books_by("David Sedaris") expect(books).to match_array( [ have_attributes( "title" => "Calypso", "publication_year" => "2018", ), have_attributes( "title" => "Theft By Finding", "publication_year" => "2017", ), ], ) ``` The outer part of the assertion is `match_array` which checks that the result is an array of a certain size with specific elements. On its own we'd have to spell out all the attributes of each book, including things like `created_at` and `updated_at`. However, by combining it with `have_attributes` matchers, we are able to make the assertion over a subset of each record's attributes. ================================================ FILE: rails/check-the-current-named-log-level.md ================================================ # Check The Current Named Log Level I'm connected to a `rails console` session for an active Rails app. I want to check the current log level. ```ruby > Rails.logger.level => 1 ``` The `1` doesn't mean much to me at a glance. I can translate that to the severity level using the `Logger::SEV_LABLE` constant. ```ruby [44] pry(main)> Logger::SEV_LABEL[Rails.logger.level] => "INFO" ``` Ah yes, `INFO`, that makes sense as the default. I can see all the severity levels by inspecting the constant itself. ```ruby [45] pry(main)> Logger::SEV_LABEL => ["DEBUG", "INFO", "WARN", "ERROR", "FATAL", "ANY"] ``` As I convenience, I can set the label using the index, the string, or even a symbol. ```ruby > Rails.logger.level => 1 > Rails.logger.level = "WARN" => "WARN" > Rails.logger.level => 2 > Rails.logger.level = :debug => :debug > Rails.logger.level => 0 ``` See the [Debugging Rails Applications guide](https://guides.rubyonrails.org/debugging_rails_applications.html#log-levels) for more details. ================================================ FILE: rails/clean-up-memory-hungry-rails-console-processes.md ================================================ # Clean Up Memory Hungry Rails Console Processes I noticed (using `htop`) that a remote server hosting a Rails app had most of its RAM being actively consumed. This was hindering my ability to run a fresh deploy because the deploy processes had to do a ton of memory swapping which drastically slowed the whole thing down. With some investigation, I discovered that most of the memory was being consumed by a handful of `rails console` processes. I didn't have any known active `rails console` processes that I was using. That combined with the dates of these processes starting way in the past suggested to me that these were abandoned processes that hadn't been properly cleaned up. ```bash server:~# ps aux | grep rails 32767 878915 0.0 0.0 1227160 936 pts/0 Ssl+ 2025 0:03 /exec rails console 32767 878942 0.9 6.5 830996 261748 pts/0 Rl+ 2025 249:51 ruby /app/bin/rails console 32767 3004097 0.0 0.0 1227160 692 pts/0 Ssl+ 2025 0:04 /exec rails console 32767 3004129 0.9 6.4 834672 257228 pts/0 Dl+ 2025 406:31 ruby /app/bin/rails console 32767 3048582 0.0 0.0 1227160 940 pts/0 Ssl+ Jan09 0:00 /exec rails console 32767 3048611 1.1 6.3 829936 253484 pts/0 Dl+ Jan09 60:50 ruby /app/bin/rails console 32767 3060033 0.0 0.0 1227160 944 pts/0 Ssl+ 2025 0:04 /exec rails console 32767 3060063 0.9 6.5 838084 260812 pts/0 Rl+ 2025 405:37 ruby /app/bin/rails console root 3699372 0.0 0.0 7008 1300 pts/0 S+ 15:51 0:00 grep --color=auto rails server:~# ps aux | grep 'rails console' | awk '{sum+=$6} END {print sum/1024 " MB"}' 1014.64 MB ``` As we can see by tacking on this `awk` command, these processes are consuming 1GB of memory. Each of these is a pair of processes. A parent process (`/exec rails console`) that kicks off and supervises the memory-hungry child process (`ruby /app/bin/rails console`). To free up this memory, I targeted each of the parent processes with a `kill` command one by one. For example: ```bash server:~# kill 878915 ``` I suspect that I may have left the occasional terminal tab open with one of these `rails console` processes running and the SSH connection was getting killed without the `rails console` getting killed with it. ================================================ FILE: rails/code-statistics-for-an-application.md ================================================ # Code Statistics For An Application Rails applications and engines provide `stats`, a rake task for reporting high level code statistics. Running it on a small project of mine generated the following report: ``` $ rake stats +----------------------+-------+-------+---------+---------+-----+-------+ | Name | Lines | LOC | Classes | Methods | M/C | LOC/M | +----------------------+-------+-------+---------+---------+-----+-------+ | Controllers | 179 | 143 | 4 | 20 | 5 | 5 | | Helpers | 18 | 16 | 0 | 2 | 0 | 6 | | Models | 30 | 22 | 3 | 2 | 0 | 9 | | Mailers | 0 | 0 | 0 | 0 | 0 | 0 | | Javascripts | 53 | 35 | 0 | 6 | 0 | 3 | | Libraries | 0 | 0 | 0 | 0 | 0 | 0 | | Controller specs | 22 | 16 | 0 | 0 | 0 | 0 | | Decorator specs | 30 | 22 | 0 | 0 | 0 | 0 | | Feature specs | 739 | 382 | 0 | 74 | 0 | 3 | | Model specs | 70 | 55 | 0 | 0 | 0 | 0 | | Cucumber features | 412 | 293 | 0 | 0 | 0 | 0 | +----------------------+-------+-------+---------+---------+-----+-------+ | Total | 1553 | 984 | 7 | 104 | 14 | 7 | +----------------------+-------+-------+---------+---------+-----+-------+ Code LOC: 216 Test LOC: 768 Code to Test Ratio: 1:3.6 ``` ================================================ FILE: rails/columns-with-default-values-are-nil-on-create.md ================================================ # Columns With Default Values Are Nil On Create Let's say I have a `MagicLinks` model backed by `magic_links` Postgres table. Both the `id` and `token` columns are of type `UUID` and have default values of `gen_random_uuid()`. That means from the Rails-side when I go to create a `MagicLink` record, I don't have to think about specifying values for `id` or `token` -- the DB will take care of that. ```ruby > magic_link = MagicLink.create(expires_at: Time.zone.now, user: User.last) User Load (5.9ms) SELECT "users".* FROM "users" ORDER BY "users"."id" DESC LIMIT $1 [["LIMIT", 1]] TRANSACTION (0.1ms) BEGIN MagicLink Create (3.1ms) INSERT INTO "magic_links" ("user_id", "expires_at", "created_at", "updated_at") VALUES ($1, $2, $3, $4) RETURNING "id" ... > magic_link.id => "6c6dddbf-4427-407d-8dc8-eef8cb65d491" > magic_link.token => nil ``` This `create` call is translated into an `insert` SQL statement that includes a `returning` clause. For `create` it is always `returning "id"`. This means that the `UUID` value generated in Postgres-land for `id` gets passed back into the ActiveRecord instance. The `UUID` value generated for `token`, however, is not because `token` isn't specified in the `returning` clause. [source](https://github.com/rails/rails/issues/17605) ================================================ FILE: rails/comparing-datetimes-down-to-second-precision.md ================================================ # Comparing DateTimes Down To Second Precision You may have an RSpec test for your Rails codebase that asserts about the datetime a record gets saved with: ```ruby two_weeks_ago = 2.weeks.ago record = Thing.create(two_weeks_ago) expect(record.some_date_time).to eq(two_weeks_ago) ``` This comparison happens with precision down to the nanosecond. Unfortunately, depending on your operating system and backing database, you may see inconsistent results due to variations in precision. One way to deal with this, if you only care about precision down to the second, is to modify the expecationa little with the [`be_within` matcher](https://relishapp.com/rspec/rspec-expectations/docs/built-in-matchers/be-within-matcher). ```ruby two_weeks_ago = 2.weeks.ago record = Thing.create(two_weeks_ago) expect(record.some_date_time).to be_within(1.second).of(two_weeks_ago) ``` The `be_within` matcher can also be used as [a nested matcher](https://twitter.com/jbrancha/status/1213162124777869319?s=20). ================================================ FILE: rails/conditional-class-selectors-in-haml.md ================================================ # Conditional Class Selectors in Haml You can assign a class selector to a tag in HAML like so: ```ruby %div.active ``` You can conditionally assign a class selector in a concise manner like so: ```ruby %div{ class: ( "active" if @thing.active? ) } ``` You can do multiple conditional class selectors with array syntax: ```ruby %div{ class: [ ("active" if @thing.active?), ("highlight" if @thing.important?) ] } ``` [source](http://stackoverflow.com/questions/3453560/append-class-if-condition-is-true-in-haml-with-rails) ================================================ FILE: rails/convert-a-symbol-to-a-constant.md ================================================ # Convert A Symbol To A Constant If you have a symbol and need to convert it to a constant, perhaps because of some metaprogramming induced by a polymorphic solution, then you may start off on an approach like the following. In fact, I've seen a number of StackOverflow solutions like this. ```ruby :module.to_s.capitalize.constantize #=> Module ``` That is great for one-word constant names, but what about multi-word constants like `OpenStruct`. This approach will not work for the symbol `:open_struct`. We need a more general solution. The key is to ditch `#capitalize and instead use another ActiveSupport method, `#classify`. ```ruby :open_struct.to_s.classify.constantize #=> OpenStruct ``` ================================================ FILE: rails/convert-json-field-to-hash-with-indifferent-access.md ================================================ # Convert JSON Field To Hash With Indifferent Access Let's say we have an `Event` model whose backing table includes a `JSONB` (or `JSON`) field called `details`. When we access `details` in a Rails context, digging into that nested data we have to use string keys throughout. However, we may have existing related code that is dealing with this shape of data using symbol keys. This might put us in a position where we have to rework a bunch of existing code or do defensive coding like `details[:user] || details["user"]`. To avoid that, we can instead have the `Event` model override `details` converting that underlying data to `HashWithIndifferentAccess` before returning it. ```ruby class Event < ApplicationRecord def details data = super return data if data.nil? case data when Array data.map { |item| item.is_a?(Hash) ? item.with_indifferent_access : item } when Hash data.with_indifferent_access else data end end end ``` With this in place, anywhere in the codebase where we access `details` on an instance of `Event` we will be able to use string or symbol keys interchangeably. ================================================ FILE: rails/count-the-number-of-records-by-attribute.md ================================================ # Count The Number Of Records By Attribute In [Count How Many Records There Are Of Each Type](postgres/count-how-many-records-there-are-of-each-type.md), I walked through how to use SQL (in PostgreSQL) to get a count of how many records there are with each unique value in a given column. This is something I tend to do with a `type` or `status` column. We can ask the same question with Rails, with very little code. It produces a nearly identical query and the same results. ```ruby > Book.group(:status).count #=> { nil => 123, "published" => 611, "draft" => 364, "review" => 239 } ``` We've picked the `Book` model and we want it to group books by their `status`. Tacking on the `#count` at the end tells it to apply the `count` aggregate. The result is a hash of each unique value of the specified attribute (`status`) paired with the count. ================================================ FILE: rails/create-a-custom-named-references-column.md ================================================ # Create A Custom Named References Column The `t.references` and `add_reference` methods both create a foreign key column name based on the target table. For instance, ```ruby add_reference :guests, :user, foreign_key: true, index: true, null: false ``` would create a `user_id` column on the `guests` table. At times, you'll need to customize the name of the foreign key column. The Rails migration DSL supports this with the `foreign_key` option. Here is an example that shows the syntax for both a new table and an existing table. ```ruby class AddInvitedByColumnToUser < ActiveRecord::Migration[6.1] def change create_table :guests, id: :uuid do |t| t.string :email, null: false t.timestamps t.references :invited_by, type: :uuid, index: true, null: false, foreign_key: { to_table: :users } end add_reference :guests, :signed_up_as, type: :uuid, index: true, null: false, foreign_key: { to_table: :users } end end ``` The `t.references` call creates a foreign key column to the `users` table named `invited_by`. The `add_reference` call adds a `signed_up_as` foreign key column to `guests` that points to users. [source](https://stackoverflow.com/a/42056089/535590) ================================================ FILE: rails/create-a-join-table-with-the-migration-dsl.md ================================================ # Create A Join Table With The Migration DSL The Rails migration DSL comes with a helper for creating a join table between two other existing tables. Call `create_join_table` with two arguments, symbols for the names of the two tables. ```ruby def change create_join_table :tags, :posts end ``` This will create a table with id references columns to each of the tables. The `db/schema.rb` addition will look something like this: ```ruby create_table "posts_tags", id: false, force: :cascade do |t| t.bigint "tag_id", null: false t.bigint "post_id", null: false end ``` A Rails/ActiveRecord convention that comes into play for the creation of this table. 1. The name should be the pluralized versions of the two joined table names. 2. The joined table names should show up in the table name in alphabetical order. Notice that despite listing `:tags` before `:posts` it creates a table called `posts_tags`. The DSL handles that for us. Some `create_join_table` defaults to be aware of: - It doesn't generate foreign key constraints. - It uses `bigint` (or `int`) for the keys (even if those tables use UUIDs). - The references are named after their respective table names. ================================================ FILE: rails/create-table-with-bigint-id-as-primary-key.md ================================================ # Create Table With bigint ID As Primary Key When creating a new table with an ActiveRecord migration, we specify all the fields _except_ the `id`. The `id`, which is the primary key, is implicit. We get it by default. The type of that `id` defaults to `int` which is a 32-bit signed integer. We can override the type of `id` in a variety of ways. The one I prefer in most cases is to make the `id` of type `bigint`. This is a 64-bit signed integer. It offers quite a bit more headroom for the number of unique identifies in our table. This can be specified by including `id: :bigint` as an option to the `create_table` method. ```ruby class CreatePosts < ActiveRecord::Migration[8.0] def change create_table :posts, id: :bigint do |t| t.string :title, null: false t.string :body, null: false t.timestamps end end end ``` [source](https://api.rubyonrails.org/v7.1/classes/ActiveRecord/ConnectionAdapters/SchemaStatements.html#method-i-create_table) ================================================ FILE: rails/creating-records-of-has-one-associations.md ================================================ # Creating Records of Has_One Associations When working with a model, say a User, that has a `has_many` association with another model, say a Post, you can create a new post for a user like so: ```ruby u1 = User.first => # u1.posts.create(title: "Some Title", content: "...") => # ``` What about with a `has_one` association? Consider a Customer that has a `has_one` association with an Account. Rails provides this method for you: ```ruby c1.create_account(account_number: 123, ...) => # ``` Rails also gives you a similar `build` method: ```ruby c1.build_account(account_number: 123, ...) => # ``` ================================================ FILE: rails/custom-validation-message.md ================================================ # Custom Validation Message When using Rails validations, a standard error message will be provided whenever there is a violation. Consider the scenario when there is a uniqueness validation on the email attribute and it is violated: ```ruby # User model validates_uniqueness_of :email # Users controller new_user.errors.full_messages #=> ["Email has already been taken"] ``` Sometimes you don't want the default validation message. The validation declaration can be given a `message` option to specify an alternate validation message. ```ruby # User model validates_uniqueness_of :email, message: 'is not available' # Users controller new_user.errors.full_messages #=> ["Email is not available"] ``` Keep in mind that `full_messages` will prepend the model name to the front of the message. You'll want to ensure that the resulting message is coherent. ================================================ FILE: rails/customize-paths-and-helpers-for-devise-routes.md ================================================ # Customize Paths And Helpers For Devise Routes Wih a default Devise setup (`devise_for :users`), the sign up/in/out routes are located at `/users/sign_up`, `/users/sign_in`, and `/users/sign_out`. And the path helpers are `new_user_registration_path`, `new_user_session_path`, and `destroy_user_session_path`, respectively. These can be customized in `config/routes.rb` by opening up the `devise_scope :user` block and re-specifying the routes of interest. ```ruby Rails.application.routes.draw do devise_for :users devise_scope :user do get 'sign_up', to: 'devise/registrations#new' get 'sign_in', to: 'devise/sessions#new' delete 'sign_out', to: 'devise/sessions#destroy' end end ``` These three custom routes override the paths and helps I described above like so: - `sign_up_path` -> `/sign_up` - `sign_in_path` -> `/sign_in` - `sign_out_path` -> `/sign_out` I find these path helpers easier to work with and I like the UX of registration/session paths not nested under `/user`. ================================================ FILE: rails/customize-template-for-new-schema-migration.md ================================================ # Customize Template For New Schema Migration Rails has a set of generator functionality that we can use to scaffold entire slices of an app all the way down to generating a single migration file. ```bash $ rails generate migration MakeUserStatusColumnNotNull ``` When we run a migration generator command like that, Rails reaches for the [baked-in migration template](https://github.com/rails/rails/blob/92be9af152f721588b7414119c931ea92930947b/activerecord/lib/rails/generators/active_record/migration/templates/migration.rb.tt) and creates a new migration file based on the given name and any other local variables that get set internally. That's the standard behavior. However, we can override the migration template by defining our own template in `lib/templates/migration.rb.tt` of our Rails app. We'll need to follow the basic structure, but then we can alter it to our needs. For instance, I typically like to use the `#up` and `#down` methods and write raw SQL for my migrations. To help with that this template provides a good starting point. ```ruby class <%= migration_class_name %> < ActiveRecord::Migration[<%= ActiveRecord::Migration.current_version %>] def up execute <<~SQL SQL end def down execute <<~SQL SQL end end ``` We can see in [`migration_generator.rb`](https://github.com/rails/rails/blob/92be9af152f721588b7414119c931ea92930947b/activerecord/lib/rails/generators/active_record/migration/migration_generator.rb#L26-L43) how locals get set and what template gets chosen. ================================================ FILE: rails/customize-the-path-of-a-resource-route.md ================================================ # Customize The Path Of A Resource Route The `:path` option allows you to customize the path used by a resource route in Rails' `config/routes.rb` file. This is handy if you have a multi-word resource that you would like to use dashes in the path. For instance ```ruby resources :audio_books ``` would have paths like `/audio_books` and `/audio_books/:id`. By specifying the `:path` option ```ruby resources :audio_books, path: 'audio-books' ``` the paths end up like `/audio-books` and `/audio-books/id`. [source](https://stackoverflow.com/questions/5334465/routes-with-dash-instead-of-underscore-in-ruby-on-rails) ================================================ FILE: rails/define-the-root-path-for-the-app.md ================================================ # Define The Root Path For The App The `root_path` helper that you might want to use in Rails controllers and views is not available by default. ```ruby > Rails.application.routes.url_helpers.root_path ruby/3.2.2/lib/ruby/gems/3.2.0/gems/irb-1.14.0/lib/irb.rb:1285:in `full_message': undefined method `root_path' for # (NoMethodError) Rails.application.routes.url_helpers.root_path ^^^^^^^^^^ Did you mean? logout_path book_path ``` It needs to be declared in the `config/routes.rb` file with the controller action that it points to. ```ruby # config/routes.rb Rails.application.routes.draw do root 'home#index' end ``` Once this is defined the `root_path` will now be available with the rest of your URL helpers. ```ruby better-reads(dev)> reload! Reloading... better-reads(dev)> Rails.application.routes.url_helpers.root_path => "/" ``` [source](https://guides.rubyonrails.org/routing.html#using-root) ================================================ FILE: rails/delete-paranoid-records.md ================================================ # Delete Paranoid Records The [ActsAsParanoid gem](https://github.com/ActsAsParanoid/acts_as_paranoid) provides soft delete functionality to `ActiveRecord` objects in Rails. You can enhance a model with its functionality like so: ```ruby class User < ActiveRecord::Base acts_as_paranoid end ``` This gem hijacks `ActiveRecord`'s standard `destroy` and `destroy!` functionality. If you call either of these methods, instead of the record being deleted from the database, it's `deleted_at` column is updated from `nil` to the current timestamp. Resulting in a _soft deleted_ record. If you call `destroy` or `destroy!` a second time (i.e. on a record that has already been soft deleted), it will be actually deleted from the database. Alternatively, you can call `destroy_fully!` from the beginning to skip the soft delete. ================================================ FILE: rails/demodulize-a-class-name.md ================================================ # Demodulize A Class Name If you call `.class.name` on an instance of some class, the fully qualified name will be returned, module names and all. Consider the following example class: ```ruby module One module Two class Three ... end end end ``` ```ruby > One::Two::Three.new.class.name #=> "One::Two::Three" ``` If you just want the unqualified class name; modules not included, you can use the `#demodulize` method provided by `ActiveSupport`. ```ruby > One::Two::Three.new.class.name.demodulize #=> "Three" ``` ================================================ FILE: rails/determine-the-configured-primary-key-type.md ================================================ # Determine The Configured Primary Key Type I noticed an interesting helper function in the database migration generated by `bin/rails active_storage:install`. ```ruby class CreateActiveStorageTables < ActiveRecord::Migration[8.0] def change # Use Active Record's configured type for primary and foreign keys primary_key_type, foreign_key_type = primary_and_foreign_key_types # ... end private def primary_and_foreign_key_types config = Rails.configuration.generators setting = config.options[config.orm][:primary_key_type] primary_key_type = setting || :primary_key foreign_key_type = setting || :bigint [ primary_key_type, foreign_key_type ] end end ``` The `primary_and_foreign_key_types` method looks in the generators config for the ORM (`:active_record`) to determine the configured `:primary_key_type`. By default this will return `nil`. This method then uses `:primary_key` as a fallback value which will be `bigint`. That's why the `foreign_key_type` falls back to `:bigint`. If desired, this can be manually configured in `config/application.rb` like shown in the [ActiveRecord Migrations docs](https://guides.rubyonrails.org/active_record_migrations.html#enabling-uuids-in-rails). ================================================ FILE: rails/different-ways-to-add-a-foreign-key-reference.md ================================================ # Different Ways To Add A Foreign Key Reference A foreign key reference creates a relationship between two tables that is guaranteed by a foreign key constraint. This is a minimal example. ```ruby create_table :books t.references :author, foreign_key: true end ``` The `foreign_key: true` is needed here, otherwise just the reference column is created without a backing constraint. When `foreign_key` is true, an index will be created for the column as well. This is a maximal example. ```ruby create_table :books t.references :author, index: true, foreign_key: true, type: :uuid, null: false end ``` It is explicit about the foreign key and index. It specifies a `not null` constraint. It declares the type as `uuid` assuming the `authors` table's primary key is of type `uuid`. Here is an example with a custom column name. ```ruby create_table :books t.references :written_by, foreign_key: { to_table: :authors } end ``` Here is adding a reference to an existing table. ```ruby def up add_reference :books, :author, index: true, foreign_key: true end ``` There are more combinations of these, but I hope there is enough here to be able to iterate to a solution that works for you. ================================================ FILE: rails/disambiguate-where-in-a-joined-relation.md ================================================ # Disambiguate Where In A Joined Relation When you join two tables using ActiveRecord ```ruby Post.joins(:author) ``` You get a relation that involves both of the tables. This allows you to write queries that work off the data in that join. The primary table in the join is `posts`. Any column references in a `#where` call will be assumed to be related to `posts`. If you want to reference a column on the `authors` table, you'll need to provide that specificity. The hash syntax for `#where` is a great way to do that: ```ruby Post.joins(:author).where({ authors: { name: "John Steinbeck" }}) ``` You can also use the string syntax: ```ruby Post.joins(:author).where("authors.name = ?", "John Steinbeck") ``` [source](https://apidock.com/rails/v4.2.7/ActiveRecord/QueryMethods/where) ================================================ FILE: rails/empty-find-by-returns-first-record.md ================================================ # Empty find_by Returns First Record During a RubyConf 2024 talk, a speaker mentioned that if you pass `nil` to [ActiveRecord's `#find_by` method](https://api.rubyonrails.org/classes/ActiveRecord/FinderMethods.html#method-i-find_by), it will return the first record from the database. This is a bit unintuitive, so lets look at an example and then I'll show you why. ```ruby > Book.first #=> # > Book.find_by(nil) #=> # ``` So, that is the same object in both cases, but why? Our first hint is in the SQL that gets constructed when making that method call. ```ruby Book Load (2.5ms) SELECT "books".* FROM "books" LIMIT $1 [["LIMIT", 1]] ``` It's grabbing all books and limiting to _one_ result. Lets look at the underlying implementation of the `#find_by` method. ```ruby # File activerecord/lib/active_record/relation/finder_methods.rb, line 111 def find_by(arg, *args) where(arg, *args).take end ``` Sure enough, the implementation is a `#where` followed by a `#take`. Since the `#where` is receiving `nil` as its `arg`, there are no conditions _filtering_ the query. And the `#take` corresponds to the `limit 1`. Knowing that, we can understand that we will also get the first record from the database if we call `#find_by` with `{}`. Again, no conditions to filter on, so give me all books limited to one. One small caveat: notice how there is no `order by` clause in the above SQL output. This differs from `Books.first` which implicitly does an order on the `id` column. Though these method are likely to return the same result, the ordering of `#find_by` is not guaranteed to be the same without an `order by` clause. ================================================ FILE: rails/enforce-locals-passed-to-a-partial.md ================================================ # Enforce Locals Passed To A Partial I have a big form partial (`_form.html.erb`) that is rendered by several different _new_ and _edit_ views. It's hard to tell at a glance, but the partial requires that `blogmark` and `cancel_path` are included as locals when it is rendered. As of [Rails 7.1](https://github.com/rails/rails/pull/45602), we now have a built-in way to enforce locals passed to a partial. We can add a magic comment at the top of the partial that specifies the locals: ```ruby <%# locals: (blogmark:, cancel_path:) -%> <%= form_with(model: blogmark, local: true, class: "w-full max-w-3xl mb-8") do |form| %> <% ... %> <% end %> ``` This particular ERB magic comment declares that [`blogmark`](https://still.visualmode.dev/blogmarks) and `cancel_path` are required locals. So, what happens if I have a `new.html.erb` view that looks like this: ```ruby

    New Blogmark

    <%= render 'blogmarks/form', blogmark: @blogmark %> ``` When I try to view the page, an error is raised: ``` Showing /Some/path/app/views/blogmarks/_form.html.erb where line # raised: missing local: :cancel_path for app/views/blogmarks/_form.html.erb ``` Updating the `render` statement to include a `cancel_path` local fixes the issue. [source](https://gorails.com/episodes/template-locals-in-rails-7-1) ================================================ FILE: rails/ensure-a-rake-task-cannot-write-data.md ================================================ # Ensure A Rake Task Cannot Write Data Let's say we are writing a substantially complex rake task for aggregating, arranging, and exporting data from our app's database. The idea is to run this rake task against production. There is no part of this rake task that needs to write data. It should act as a read-only script. And in fact, we can ensure it isn't able to write any data. We can do that by putting `ApplicationRecord` in read-only mode. ```ruby task big_export_rake_task: :environment do class ApplicationRecord def readonly? true end end # a bunch of logic ... puts 'this gets executed' # Call method that inadvertently writes data User.update(email: 'readonly@email.com') # more logic ... puts 'this does not get executed' end ``` Because we have made all of `ApplicationRecord` read-only, when we run this task, it is immediately going to rollback the changes that were attempted and then raise an error which aborts the rest of the rake task. We'll see some messaging like this: ``` rake aborted! ActiveRecord::ReadOnlyRecord: User is marked as readonly ``` h/t [Dillon Hafer](https://dillonhafer.com/) ================================================ FILE: rails/ensure-migrations-use-the-latest-schema.md ================================================ # Ensure Migrations Use The Latest Schema Real-world migrations in Rails apps can sometimes involve both schema changes and data changes. For instance, if you are moving a column from one table to another, you'll need to add a new column, move some data, and then delete the old column. ```ruby # Assume the following are defined: # GenericAuthor for table 'authors' # GenericBook for table 'books' def up add_column :books, :genre, :string GenericAuthor.find_each do |author| book = GenericBook.find_by(author_id: author.id) book.update!(genre: author.genre) end remove_column :authors, :genre end ``` This migration looks straightforward, but you may find that no data actually gets transferred to the `genre` column on `books`. This is because as a performance optimization, Rails has cached the scema. Thus an `ActiveRecord` modification like `book.update!` will be working off a version of the schema that doesn't include `genre` as a column. We can ensure `ActiveRecord` is using the latest column informtion for the `books` table by calling [`reset_column_information`](https://api.rubyonrails.org/classes/ActiveRecord/ModelSchema/ClassMethods.html#method-i-reset_column_information). ```ruby def up add_column :books, :genre, :string GenericBook.reset_column_information GenericAuthor.find_each do |author| book = GenericBook.find_by(author_id: author.id) book.update!(genre: author.genre) end remove_column :authors, :genre end ``` Now the update will work and `genre` will be set on `books`. ================================================ FILE: rails/ensure-record-saved-with-after-commit-callback.md ================================================ # Ensure Record Saved With after_commit Callback In my experience, some of the more common `ActiveRecord` callbacks are ones like `before_save` or `after_update`. While working with some code, where I needed to send a notification when a certain value was updated, I learned that something like `after_update` wasn't sufficient. If my record is updated within a transaction, the `after_update` will get triggered even though the changes could later get rolled back resulting in me erroneously sending the notification. ```ruby ActiveRecord::Base.transaction do user.update(interesting_value: 123) do_something # <-- rollback could happen here! end ``` To ensure I'm not over-eager with my notifications, I should instead use [`after_commit`](https://api.rubyonrails.org/v7.0.5/classes/ActiveRecord/Transactions/ClassMethods.html#method-i-after_commit) which only gets called after the changes have been committed to the database. ```ruby class User < ApplicationRecord after_commit :send_notification, on: [:create, :update], if: :interesting_value_was_changed? # rest of class... private def send_notification # logic... end def interesting_value_was_changed? # logic... end end ``` On either `create` or `update` if my condition is met, then after the _commit_ goes through, the `#send_notification` method will be triggered. ================================================ FILE: rails/filter-active-model-validation-errors.md ================================================ # Filter ActiveModel Validation Errors Now that `ActiveModel` has a custom `Errors` class (as of Rails 6.1) instead of a hash, we get some useful functionality. Namely, we get a [`#where` method](https://api.rubyonrails.org/classes/ActiveModel/Errors.html#method-i-where) that allows us to filter errors based on the attribute name, type of validation, and even properties of that validation. Here I have created a new `Book` without any attributes. All of its validations are going to fail and we are going to have an `ActiveModel::Errors` object attached to it with several errors. ```ruby > book = Book.new => # book.valid? => false > book.errors => #:required, :if=>#}>, #, #3}>, #, #]> ``` Let's say I want to check for a specific validation error. I can use `#where` to filter down by attribute name (e.g. `:title`). I can filter even further by including the validation type as well (e.g. `:too_short`). ```ruby > book.errors.where(:title) => [#, #3}>] > book.errors.where(:title, :too_short) => [#3}>] > book.errors.where(:title, :too_short).first.message => "is too short (minimum is 3 characters)" > book.errors.where(:title, :too_short).first.full_message => "Title is too short (minimum is 3 characters)" ``` This filtering could be used as part of conditional checks for what flash message gets displayed to the user or even what route/view gets rendered in response to the error. ================================================ FILE: rails/filter-active-storage-blobs-to-only-images.md ================================================ # Filter ActiveStorage Blobs To Only Images If your Rails app is using `ActiveStorage` for both images and ActionMailbox emails, then you're going to have a mix of both in the `active_storage_blobs` table. ```sql > select id, filename, content_type from active_storage_blobs limit 2; | id | filename | content_type | |----|--------------------|----------------| | 1 | shirt-brothers.jpg | image/jpeg | | 2 | message.eml | message/rfc822 | ``` In that case, you are going to want to make sure that any part of your system that only cares to deal with images filters down to only blobs where the `content_type` is one that you care about. I expect that there might be a couple different image `content_type` values that my system handles, so I filter my `active_storage_blobs` like so: ```ruby @images = ActiveStorage::Blob .where(content_type: %w[image/jpeg image/png image/gif image/webp]) .order(created_at: :desc) .first(10) ``` ================================================ FILE: rails/find-or-create-a-record-with-factory-bot.md ================================================ # Find Or Create A Record With FactoryBot I have a bunch of tests throughout my test suite that rely on a particular kind of unique record. Let's say it is a special admin user. ```ruby admin = FactoryBot.create(:user, email: 'admin@company.com') ``` If this user has already been created then trying to re-create it with [FactoryBot](https://github.com/thoughtbot/factory_bot) will result in a unique email validation error. Another way to approach this would be to either find or create the admin user. In some standard Rails code that might look like this: ```ruby admin = User.find_by(email: 'admin@company.com') || FactoryBot.create(:user, email: 'admin@company.com') ``` There is some repetitiveness to this that I'd like to avoid. FactoryBot doesn't have an equivalent to ActiveRecord's `find_and_create_by`, but we can work around this. We can add an `initialize_with` directive to the `User` factory. ```ruby FactoryBot.define do factory :user do sequence(:email) { |n| 'user#{n}@example.com' } # a bunch of other attributes initialize_with { User.find_or_create_by(email: email) } end end ``` With this in place, we can call `FactoryBot.create` with the already existing _admin_ user and it will look up the record instead of raising a validation error. [source](https://stackoverflow.com/a/11799674/535590) ================================================ FILE: rails/find-records-with-multiple-associated-records.md ================================================ # Find Records With Multiple Associated Records Relational data often involves a table that has a one-to-many relationship with another table. For instance, a team can be made up of many members. A question we may want to ask of that data is, what are the records (`teams`) that are associated with more than one of this other table (`members`). With a few SQL features that are supported by ActiveRecord's query syntax, we can answer that question. To make it interesting, let's say we are trying to answer the question, "what are the teams that have multiple _active_ members?" ```ruby Team .joins(:members) .where(members: { status: 'active' }) .having("count(*) >= 2") .group("teams.id") .count => { 123 => 2, 345 => 3, 567 => 2, ... } ``` That final `.count` is going to manifest as a `count(*)` in the `select` clause. That `count(*)` aggregate combined with the `group("teams.id")` is going to flatten the results to be unique by team ID. Then the `having` clause will filter out all teams with a member count less than 2. And before that, the where will cut down the members to only those that are `active`. If you just want the IDs, you can tack a `#keys` call onto the end of that query result. ================================================ FILE: rails/force-all-users-to-sign-out.md ================================================ # Force All Users To Sign Out If you are using cookie-based authentication and you want to sign out all users (so that they have to re-authenticate), you need to invalidate all of the cookies. Because the cookies live in the client's browser, you cannot simply clear them like you would with session-based authentication. Instead, you need to replace the session token used to create all those cookies. First, get a new token: ```bash $ bundle exec rake secret 538696c1399ff182486e09980ba915d098b8fb23a3ace42c3eea0ab51b18fdff7895cd620f32b263d10d25c2fdba16647f4d8632e9032eccef7406e1ad9cba09 ``` Then, replace the current `secret_key_base` value with that new secret token [wherever it is stored](https://api.rubyonrails.org/classes/Rails/Application.html#method-i-secret_key_base) in the production environment. [source](https://stackoverflow.com/questions/35190591/rails-4-devise-how-to-log-out-all-users) ================================================ FILE: rails/format-datetime-with-builtin-formats.md ================================================ # Format DateTime With Builtin Formats The Rails [`Date`](https://api.rubyonrails.org/classes/Date.html)/`DateTime` and [`Time`](https://api.rubyonrails.org/classes/Time.html) classes each come with a `DATE_FORMATS` constant that is a hash of symbol names to format strings. ```ruby > DateTime::DATE_FORMATS => {:short=>"%d %b", :long=>"%B %d, %Y", :db=>"%Y-%m-%d", :inspect=>"%Y-%m-%d", :number=>"%Y%m%d", :long_ordinal=> #, :rfc822=>"%d %b %Y", :rfc2822=>"%d %b %Y", :iso8601=> #} ``` These can be used as a standardized, ready-to-use, named formats when turning `DateTime` objects into strings. Here are a few examples ```ruby > now = DateTime.now => Wed, 30 Apr 2025 23:08:08 -0500 > now.to_fs(:long) => "April 30, 2025 23:08" > now.to_fs(:long_ordinal) => "April 30th, 2025 23:08" > now.to_fs(:iso8601) => "2025-04-30T23:08:08-05:00" ``` If an unrecognized key is passed to `#to_fs`, then it falls back to the `:iso8601` format. ```ruby > now.to_fs(:taco_bell) => "2025-04-30T23:08:08-05:00" ``` ================================================ FILE: rails/format-specific-html-erb-template-files.md ================================================ # Format Specific html.erb Template Files There are a few tools out there that can do formatting of `*.html.erb` template files. One that I like is [nebulab/erb-formatter](https://github.com/nebulab/erb-formatter#readme) because it is ready to use in many different editors. That means that it is easier to adopt on a team of developers with different editor preferences. That said, there are projects where I don't necessarily want it wired up to run on save because the formatting changesets will be too agressive. Instead, I want to run it manually on specific files as I see fit. To do this, I install the formatter tool: ```bash $ gem install erb-formatter ``` And now it is available as a CLI tool (try `which erb-format`). As their docs recommend, I can run it against all files like so: ``` $ erb-format app/views/**/*.html.erb --write ``` If that is too aggressive though, I find it useful to either run against a specific file: ``` $ erb-format app/views/some/model/index.html.erb --write ``` Or when I'm wrapping up changes on a branch, I like to run it against all the view files that were touched on this branch: ``` $ git diff --name-only master...HEAD -- app/views | xargs erb-format --write ``` ================================================ FILE: rails/generate-a-model.md ================================================ # Generate A Model The `rails` CLI comes with a variety of generators. Perhaps the mostly common one to use is the _model_ generator. The model generator will create a migration and a model file for the entity that you name. In the following example `Book` will result in a `app/models/book.rb` file as well as a migration file for a `books` table. These generators know the singular and plural conventions. At the end of the command is a series of field definitions containing the field _name_ and field _type_. These are used in the migration file for defining columns on the new table. ```bash ❯ bin/rails generate model Book title:string publication_date:date author:string invoke active_record create db/migrate/20240920223447_create_books.rb create app/models/book.rb invoke rspec create spec/models/book_spec.rb ``` You may also notice that an `rspec` action was invoked as part of this generator. That is because I have the `rspec-rails` gem in my project. That gem hooks into the model generator so that a model spec also gets generated. Handy! ================================================ FILE: rails/generate-a-rails-app-from-the-main-branch.md ================================================ # Generate A Rails App From The Main Branch Typically you are going to want to generate a Rails app using some officially released version of the framework. These releases have been thoroughly tested, have received patches, and can guarantee a certain level of stability. However, if you are wanting to try out the latest, unreleased features, you may want to generate a fresh Rails app based off the current state of the `main` branch of the `rails` repository. To do this, add the `--main` flag: ```bash $ rails new rails_app_on_main --main ``` Toward the top of your app's `Gemfile`, you'll see that `rails` is pointed to the `main` branch of their repo: ```ruby # Use main development branch of Rails gem "rails", github: "rails/rails", branch: "main" ``` See `rails new --help` for more details [source](https://x.com/gregmolnar/status/1832720168264286571) ================================================ FILE: rails/generating-and-executing-sql.md ================================================ # Generating And Executing SQL Rails' ActiveRecord can easily support 90% of the querying we do against the tables in our database. However, there is the occasional exceptional query that is more easily written in SQL -- perhaps that query cannot even be written with the ActiveRecord DSL. For these instances, we need a way to generate and execute SQL safely. The [`sanitize_sql_array`](http://api.rubyonrails.org/classes/ActiveRecord/Sanitization/ClassMethods.html#method-i-sanitize_sql_array) method is invaluable for this. First, let's get a connection and some variables that we can use downstream in our query. ```ruby > conn = ActiveRecord::Base.connection => # > one, ten = 1, 10 => [1, 10] ``` Now, we are ready to safely generate our SQL query as a string. We have to use `send` because it is not publicly available. Generally, this is frowned upon, but in my opinion it is worth breaking the private interface to ensure our SQL is sanitized. ```ruby > sql = ActiveRecord::Base.send(:sanitize_sql_array, ["select generate_series(?, ?);", one, ten]) => "select generate_series(1, 10);" ``` Lastly, we can execute the query with our connection and inspect the results. ```ruby > result = conn.execute(sql) (0.4ms) select generate_series(1, 10); => # > result.to_a => [{"generate_series"=>1}, {"generate_series"=>2}, {"generate_series"=>3}, {"generate_series"=>4}, {"generate_series"=>5}, {"generate_series"=>6}, {"generate_series"=>7}, {"generate_series"=>8}, {"generate_series"=>9}, {"generate_series"=>10}] ``` ================================================ FILE: rails/get-a-quick-approximate-count-of-a-large-table.md ================================================ # Get A Quick Approximate Count Of A Large Table Let's say our Rails app has a massive `events` table in it's Postgres database. We might be tempted to reach for an ActiveRecord API method like `Event.count` to get the number of records in the table. For tables with millions of rows, this is going to be slow. If all we need is an approximate count, there is a faster way that uses some of PostgreSQL's internal bookkeeping. We can request the approximate number of tuples recorded for our table by name. This query can be processed as raw SQL by the `#execute` method available on `ActiveRecord::Base.connection`. ```ruby ActiveRecord::Base.connection.execute(<<~SQL) select reltuples::numeric as count from pg_class where relname='events'; SQL ``` That is going to spit out the `PG::Result` object which doesn't look like much on its own. ``` # ``` If we tack on a couple other methods, we can get the count as our result. ```ruby ActiveRecord::Base.connection.execute(<<~SQL).to_a.first["count"].to_i select reltuples::numeric as count from pg_class where relname='events'; SQL ``` ================================================ FILE: rails/get-active-record-attribute-directly-from-database.md ================================================ # Get ActiveRecord Attribute Directly From Database In Rails, an ActiveRecord model will automatically get methods named after each column in the backing database table. This can be called to retrieve those values from the respective columns in the database. What if you wanted to override and alter one of those values? For example, ensure the `email` value you're passing around is always fully downcased. Something like this won't quite work. ```ruby def email email.downcase end ``` Because the method is named `email`, the `email` reference inside it will call itself, recursively, until it exceeds the stack. Instead, you need a way of referencing the email attribute that is stored in the database. [`attribute_in_database`](https://api.rubyonrails.org/classes/ActiveRecord/AttributeMethods/Dirty.html#method-i-attribute_in_database) will do the trick. ```ruby def email attribute_in_database('email').downcase end ``` That will retrieve the value from the `email` column in the database for this record, downcase it, and return it. Anyone calling `email` won't notice the difference. h/t [Dillon Hafer](https://twitter.com/dillonhafer) ================================================ FILE: rails/get-an-array-of-values-from-the-database.md ================================================ # Get An Array Of Values From The Database We generally get data from our database through [ActiveRecord models](https://api.rubyonrails.org/classes/ActiveRecord/Base.html): ```ruby > Product.where(available: true).pluck(:sku) [ "efg-1234", "pqr-3455", ... ] ``` If we need to do a more specialized query, we might reach for [`execute`](https://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/DatabaseStatements.html#method-i-execute): ```ruby > ActiveRecord::Base.connection.execute(<<-SQL) select split_part(sku, '-', 1) product_type from products where available = true; SQL [{ "product_type" => "efg" }, { "product_type" => "pqr" }, ... ] ``` The results are bundled up in a predictable, but verbose array of hashes. We could trim the result down to just the values using either [`select_values`](https://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/DatabaseStatements.html#method-i-select_values) or [`select_rows`](https://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/DatabaseStatements.html#method-i-select_rows): ```ruby > ActiveRecord::Base.connection.select_values(<<-SQL) select split_part(sku, '-', 1) product_type from products where available = true; SQL [ "efg", "pqr", ... ] ``` If the SQL statement is to return more than one row in the result, then you'll want `select_rows` instead of `select_values`. ================================================ FILE: rails/get-an-empty-activerecord-relation.md ================================================ # Get An Empty ActiveRecord Relation When you query for something (with `#where`) and there are no results matching that query, you get something that looks like an empty array (`[]`), but it's not quite. ```ruby > result [] > result.class Book::ActiveRecord_Relation ``` It's an empty [`ActiveRecord` relation](https://api.rubyonrails.org/classes/ActiveRecord/Relation.html). You can get an instance of an empty `ActiveRecord` relation without constructing a _no result_ query. ```ruby > Book.none [] > Book.none.class Book::ActiveRecord_Relation ``` I can think of a couple scenarios where this would be useful: - as a default value for a method parameter - as a test value for a method that expects to have an `ActiveRecord` relation passed in [source](https://stackoverflow.com/questions/4877931/how-to-return-an-empty-activerecord-relation) ================================================ FILE: rails/get-formatted-utc-offset-value.md ================================================ # Get Formatted UTC Offset Value The UTC offset (during DST) for Chicago is `-05:00`. And for Newfoundland it is `-02:30`. Rails has an [`ActiveSupport::TimeZone` module](https://api.rubyonrails.org/v7.1.3.2/classes/ActiveSupport/TimeZone.html#method-c-seconds_to_utc_offset) that can turn the UTC offset in seconds into this formatted value. ```ruby > chicago = TZInfo::Timezone.get('America/Chicago') => # > ActiveSupport::TimeZone.seconds_to_utc_offset(chicago.utc_offset) => "-05:00" > newfoundland = TZInfo::Timezone.get('America/St_Johns') => # > ActiveSupport::TimeZone.seconds_to_utc_offset(newfoundland.utc_offset) => "-02:30" ``` The underlying `tzinfo` gem is DST-aware and its database even knows which time zone identifiers don't observe DST, so you can run this anytime of the year and expect reliable results. Here is another way at this: ```ruby > chicago = TZInfo::Timezone.get('America/Chicago') => # > ActiveSupport::TimeZone[chicago.name] => #, @utc_offset=nil> > ActiveSupport::TimeZone[chicago.name].formatted_offset => "-06:00" > ActiveSupport::TimeZone[chicago.name].formatted_offset(false) => "-0600" ``` ================================================ FILE: rails/get-help-with-a-rails-app-update.md ================================================ # Get Help With A Rails App Update Rails version upgrades can be pretty involved. The ecosystem and the framework are under constant evolution. While each patch version will stay reliably stable, as soon as you go to do a minor or major upgrade (which you should stay on top of), you'll have lots to consider. Rails helps with this via the `app:update` rake task. When run, it will prompt you with a series of files that it wants to change. For each one you'll have some options. ```bash $ rails app:update Overwrite my-app/config/boot.rb? (enter "h" for help) [Ynaqdhm] h Y - yes, overwrite n - no, do not overwrite a - all, overwrite this and all others q - quit, abort d - diff, show the differences between the old and the new h - help, show this help m - merge, run merge tool ``` Start by using `d` to see a diff of the changes. If it's small and doesn't overwrite important, existing settings, then you can use `y` to accept them. For a lot of these files the changes will be too aggressive. So side-by-side with the diff, update the file manually. Then use `n` to go to the next update. ================================================ FILE: rails/get-the-column-names-for-a-model.md ================================================ # Get The Column Names For A Model An `ActiveRecord` model is backed by a table in your application's database. We can get the column names for a backing table with `ActiveRecord`s help using the `.column_names` method. ```ruby > Book.column_names => ["id", "title", "publication_year", "created_at", "updated_at", "author_id", "genre"] ``` Any `ActiveRecord` model will have access to this class method. See [the docs](https://devdocs.io/rails~5.2/activerecord/modelschema/classmethods#method-i-column_names) for more details. ================================================ FILE: rails/get-the-current-time.md ================================================ # Get The Current Time Working with time and time zones in server development can get complicated. Time-sensitive code that worked locally can unexpected fail when deployed to a server in a different time zone. Or users can end up seeing timestamps that look a few hours off. To avoid this kinds of mistakes in Rails development, we should avoid using `Time.now` and instead use `Time.current`. > Rails saves timestamps to the database in UTC time zone. We should always use > Time.current for any database queries, so that Rails will translate and > compare the correct times. ```ruby > Time.zone => #, @utc_offset=nil> > Time.now => 2021-01-28 19:22:42.312577 -0600 > Time.current => Fri, 29 Jan 2021 01:22:45.926181000 UTC +00:00 > Time.zone = 'Eastern Time (US & Canada)' => "Eastern Time (US & Canada)" > Time.now => 2021-01-28 19:23:28.255106 -0600 > Time.current => Thu, 28 Jan 2021 20:23:32.150545000 EST -05:00 ``` My server's default time zone is UTC. `Time.now` gives me my computer's system time (Central Time). `Time.current` gives me the time in UTC. If I then change the server's time zone to Eastern Time, `Time.now` still offers up my system time whereas `Time.current` produces the current time in Easter Time. [source](https://thoughtbot.com/blog/its-about-time-zones) ================================================ FILE: rails/grab-a-random-record-from-the-database.md ================================================ # Grab A Random Record From The Database I recently learned of a clever way to grab a random record for a particular model from the database. This is handy if you are poking around in the database to see what some records look like. Order the records for that table randomly and then grab the first. ```ruby Event.order('random()').first ``` This grabs a random `Event` record from the `events` table. Note, however, that for Rails 6+, this approach won't work. Because of some extra safety measures around executing raw SQL, you'll instead have to write the above as: ```ruby Event.order( Arel.sql('random()') ).first ``` This uses [`Arel.sql`](https://api.rubyonrails.org/classes/Arel.html#method-c-sql) to mark `'random()'` as a known-safe string of SQL. Because we are explicitly passing a string that represents a known-safe function call, this is fine. Take care to not pass any user-generated SQL in a scenario like this unless you know what you're doing. [source](https://stackoverflow.com/a/49525874/535590) ================================================ FILE: rails/handle-named-arguments-in-a-rake-task.md ================================================ # Handle Named Arguments In A Rake Task There are [a number of ways](https://www.seancdavis.com/blog/4-ways-to-pass-arguments-to-a-rake-task/) to pass arguments into a Rake task. The standard approach only allows for positional arguments. When I need named and optional arguments, my preferred approach is to use environment variables. Here is a skimmed down version of a user lookup task. Notice the task definition itself doesn't include any arguments. Instead, a couple optional values are extracted from the environment (`ENV`) at the beginngin of the task. ```ruby desc "An example task with named, optional arguments" task :lookup_user => :environment do user_id = ENV['USER_ID'] email = ENV['EMAIL'] if user_id.present? user = User.find(user_id) if user.blank? puts "No user for id ##{user_id}" end elsif email.present? user = User.find_by(email: email) if user.blank? puts "No user for email #{email}" end end puts "User found" if user.present? end ``` This task can be invoked in the following ways: ```bash $ rake lookup_user USER_ID=123 ``` ```bash $ rake lookup_user EMAIL="user@example.com" ``` or even with both arguments included, in which case the task has been written to give precedence to `USER_ID`: ```bash $ rake lookup_user EMAIL="user@example.com" USER_ID=123 ``` ================================================ FILE: rails/hash-slicing.md ================================================ # Hash Slicing Rails' ActiveSupport adds [`#slice`](http://api.rubyonrails.org/classes/Hash.html#method-i-slice) and [`#slice!`](http://api.rubyonrails.org/classes/Hash.html#method-i-slice-21) to the `Hash` class. The interface of these two methods seems a little inconsistent though. ```ruby > {a: 1, b: 2, c: 3}.slice(:a) => {:a=>1} ``` The `#slice` method returns what is being sliced. ```ruby > {a: 1, b: 2, c: 3}.slice!(:a) => {:b=>2, :c=>3} ``` The `#slice!` method, on the other hand, returns what is being excluded. ================================================ FILE: rails/ignore-poltergeist-javascript-errors.md ================================================ # Ignore Poltergeist JavaScript Errors Poltergeist with PhantomJS (<2.0) does not support JavaScript's `bind()` method. This means that when executing an integration test that exercises JavaScript with the `bind()` method, an error will occur. If you cannot simply upgrade to a version of PhantomJS that supports `bind()`, then what can you do? Ignore the error! This can be achieved by placing the following rescue block in the appropriate place. ```ruby rescue Capybara::Poltergeist::JavascriptError ``` Use this in moderation. You want to make sure you don't ignore actual JavaScript errors. [source](http://stackoverflow.com/questions/22020680/temporarily-set-js-errors-to-false-in-poltergeist) ================================================ FILE: rails/include-devise-helpers-in-your-controller-tests.md ================================================ # Include Devise Helpers In Your Controller Tests For past versions of Devise, you could include `DeviseHelpers` as part of `controller` or `request` type tests to have access to the `sign_in` and `sign_out` helpers. ```ruby RSpec.configure do |config| config.include Devise::TestHelpers, :type => :controller end ``` As of [Devise 4.2.0+](https://github.com/heartcombo/devise/blob/master/CHANGELOG.md#420---2016-07-01), the `Devise::TestHelpers` have been deprecated. The `Devise::Test::ControllerHelpers` module should instead be included. ```ruby RSpec.configure do |config| config.include Devise::Test::ControllerHelpers, :type => :controller end ``` [source](https://github.com/heartcombo/devise/blob/98fc5e8e396b66b826528811287ea6680a6d0757/lib/devise/test/controller_helpers.rb#L26) ================================================ FILE: rails/inspect-configuration-of-database-connection.md ================================================ # Inspect Configuration Of Database Connection There are a lot of factors that can effect the database configuration values. - What are the settings in each environment in `config/database.yml`? - Is there any dynamic ERB code in `config/database.yml`? - Is `DATABASE_URL` set in the current environment? - Is any other code overriding these settings? To check the current _configuration hash_ for the database connection at runtime, we can run the following statement: ```ruby > ActiveRecord::Base.connection.pool.db_config.configuration_hash => {:adapter=>"postgresql", :encoding=>"unicode", :host=>"::1", :user=>"postgres", :password=>"postgres", :pool=>5, :database=>"still_development", :port=>9875} ``` In this case, I'm running the statement from the Rails console of my app's development environment. I could even access and print these values as part of debugging in a production environment with a rake task: ```ruby # In lib/tasks/debug.rake namespace :debug do task :db_config => :environment do puts "==== Database Configuration Debug ====" puts "DATABASE_URL: #{ENV['DATABASE_URL']}" puts "Active Record Config: #{ActiveRecord::Base.connection.pool.db_config.configuration_hash}" puts "Raw ENV dump:" ENV.sort.each { |k,v| puts "#{k}: #{v}" if k.include?('DB') || k.include?('DATABASE') } end end ``` [source](https://api.rubyonrails.org/classes/ActiveRecord/DatabaseConfigurations/HashConfig.html) ================================================ FILE: rails/inspect-previous-changes-to-activerecord-object.md ================================================ # Inspect Previous Changes To ActiveRecord Object If you modify an ActiveRecord object, before saving it, you can inspect changes with methods like `changed?` and `_changed?`: ```ruby book.title = "The Fifth Season" book.changed? #=> true book.title_changed? #=> true book.publication_year_changed? #=> false book.changes #=> { "title" => ["Original Title", "The Fifth Season"] } ``` After saving an object, it will no longer be in a _dirty_ state and these methods will have no _changes_ to return. If you have a reference to the saved ActiveRecord object, you can look at the _previous_ changes with methods like `previous_changes` and `_previously_changed?`: ```ruby book.title = "The Fifth Season" book.save book.title_previously_changed? #=> true book.previous_changes #=> { "title" => ["Original Title", "The Fifth Season"] } ``` [source](https://api.rubyonrails.org/classes/ActiveModel/Dirty.html) ================================================ FILE: rails/link-to-the-current-page-with-query-params.md ================================================ # Link To The Current Page With Query Params The `link_to` method is an ActionView helper for generating an `a` tag within a Rails view. There are two arguments that tend to comprise this method: the link text and a path helper. ```ruby <%= link_to 'Home', root_path %> ``` The `link_to` method can be used to generate a link to the current page with query params. You can do this be providing a hash instead of a path helper as the second argument. ```ruby <%= link_to "All", {filter: 'all'} %> <%= link_to "New", {filter: 'new'} %> <%= link_to "Posted", {filter: 'posted'} %> ``` The hash can contain one or more key-value pairs which will be turned into query params and appended to the end of the current base path. If these are part of the `posts` index page, then they will render as: ```html All New Posted ``` This is a great way to create links for a Rails action that presents different data based on query params. Often this is an index page where filtering is needed. [source](https://gorails.com/episodes/rails-link-to-current-page-with-params) ================================================ FILE: rails/list-all-installable-rails-versions.md ================================================ # List All Installable Rails Versions _Here's a [screencast](https://www.youtube.com/watch?v=IizkvqGLkhU) in case you want to watch instead of read._ I [was curious](https://twitter.com/jbrancha/status/1345467867479875584?s=20) what versions of Rails were remotely available to be installed with `gem`. On its own, `gem list rails` will show all _locally_ installed gems that partially match `rails`. That'll include gems like `rspec-rails` and `sprockets-rails`. First, the `--exact` flag can be added to narrow down the results to an exact match of `rails`. Then, the `--remote` flag can be included to request `gem` look for remote versions. That is, versions available on the rubygems server. Lastly, the `--all` flag can be included to fetch all versions instead of only the latest version. Putting it all together: ```bash $ gem list rails --exact --remote --all rails (6.1.0, 6.0.3.4, 6.0.3.3, ... 0.9.0, 0.8.5, 0.8.0) ``` [source](https://stackoverflow.com/a/9146057/535590) ================================================ FILE: rails/list-the-enqueued-jobs.md ================================================ # List The Enqueued Jobs Many Rails apps need to delegate work to jobs that can be performed at a later time. Both unit and integration testing can benefit from asserting about the jobs that get enqueued as part of certain methods and workflows. Rails provides a handy helper method for checking out the set of enqueued jobs at any given time. The [`enqueued_jobs`](http://api.rubyonrails.org/classes/ActiveJob/QueueAdapters/TestAdapter.html#method-i-enqueued_jobs) method will provide a store of all the currently enqueued jobs. It provides a number of pieces of information about each job. One way to use the information is like so: ```ruby describe '#do_thing' do it 'enqueues a job to do a thing later' do Processor.do_thing(arg1, arg2) expect(enqueued_jobs.map { |job| job[:job] }).to match_array([ LongProcessJob, SendEmailsJob ]) end end ``` To use this in your Rails project, just enable the adapter in your test configuration file: ```ruby Rails.application.config.active_job.queue_adapter = :test ``` ================================================ FILE: rails/load-a-file-when-starting-rails-console.md ================================================ # Load A File When Starting Rails Console The `rails console` command uses `irb` under the hood. That means you can use any of the flags that `irb` supports when running `rails c`. One of those flags is `-r` which "causes `irb` to load the [specified] library using require." This `-r` flag can come in handy if we want to load a little bit of setup code or some utility methods for our `rails console` session. Let's say we have `vendor/show_env.rb` with this snippet of code: ```ruby puts "#{'*' * 7} #{Rails.env.upcase} #{'*' * 7} ``` And we want that to display as we open the console. We can pass the path to that file to the `-r` flag. ```bash $ rails console -- -r ./vendor/show_env.rb ``` [Notice the `--`](https://tosbourn.com/speed-up-pasting-text-into-rails-console/). That is to indicate that we are done with `console` specific arguments and anything else should be passed along to `irb`. Without the `--`, the `-r` flag won't be recognized. See `man irb` to see what other flags `irb` supports. ================================================ FILE: rails/load-records-in-batches-with-find-each.md ================================================ # Load Records In Batches With find_each The base enumerable method offered by Ruby is `#each`. If you need to interact with an array of elements, that's a method you'll reach for at some point. When working with an `ActiveRecord` collection in Rails, you should use the [`#find_each`](https://api.rubyonrails.org/v6.1.0/classes/ActiveRecord/Batches.html#method-i-find_each) method instead of `#each`. That's because under the hood it batches the records that it will load in 1000 at a time. This is important to keep your server's resource usage from exploding when requesting a ton of records. Consider a `users` table that contains 10,000 records that are _active_. ```ruby User.where(active: true).each do |user| # do something end ``` With `#each`, all 10,000 records will be loaded into memory at once as `ActiveRecord` objects. That's potentially a lot of load on the server's available memory. Then imagine the table contains 100,000 or 1,000,000 records. This can become a big problem. ```ruby User.where(active: true).find_each do |user| # do something end ``` With `#find_each`, which uses [`#find_in_batches`](https://api.rubyonrails.org/v6.1.0/classes/ActiveRecord/Batches.html#method-i-find_in_batches) under the hood, only 1000 `ActiveRecord` objects get loaded into memory at a time. If you want to exercise more control over the batching, you can use `#find_in_batches` directly. ================================================ FILE: rails/log-sql-queries-executed-by-activerecord.md ================================================ # Log SQL Queries Executed By ActiveRecord When entering ActiveRecord statements in a Rails console, it can be useful to see what SQL queries are being executed under the hood. ```ruby > Book.first Book Load (25.6ms) SELECT "books".* FROM "books" ORDER BY "books"."id" ASC LIMIT $1 [["LIMIT", 1]] => # ActiveRecord::Base.logger = Logger.new(STDOUT) ``` Enter an ActiveRecord statement, you should now be seeing the corresponding SQL queries. [source](https://stackoverflow.com/a/2936016/535590) ================================================ FILE: rails/look-up-time-zone-info-for-identifier.md ================================================ # Look Up Time Zone Info For Identifier The `ActiveSupport::TimeZone` class overrides the `#[]` method to be a lookup mechanism for IANA Time Zone Identifier strings. These are strings like `America/Chicago` (or anything else listed under `TZInfo::Timezone.all`). Let's get an instance for `America/Chicago`. ```ruby > chi = ActiveSupport::TimeZone['America/Chicago'] => #, @utc_offset=nil> ``` Notice it has a `tzinfo` instance variable that we can access. That object contains all kinds of useful things. ```ruby > chi.tzinfo.name => "America/Chicago" > chi.tzinfo.friendly_identifier => "America - Chicago" > chi.tzinfo.abbr => "CDT" > chi.tzinfo.utc_offset => -18000 > chi.tzinfo.dst? => true ``` All of these and more. Run `ls chi.tzinfo` in a `pry` session to see what else. ================================================ FILE: rails/make-a-string-attribute-easy-to-inquire-about.md ================================================ # Make A String Attribute Easy to Inquire About Have you ever been curious why Rails environment checks work the way they do? ```ruby > Rails.env #=> 'development' > Rails.env.development? #=> true ``` What is powering this is [`ActiveSupport::StringInquirer`](https://api.rubyonrails.org/classes/ActiveSupport/StringInquirer.html). And since it is part of `ActiveSupport`, you can use that functionality elsewhere in your Rails code. Let's look at an example: ```ruby class UserWithRole attr_reader :name, :role def initialize(name, role) @name = name @role = ActiveSupport::StringInquirer.new(role) end end ``` With that class defined, we can initialize a user and inquire about their role. ```ruby > user = UserWithRole.new('Bob', 'instructor') #=> > user.role.instructor? #=> true > user.role.admin? #=> false > user.role #=> 'instructor' ``` This helper class makes it much cleaner to inquire about the role of a user. Notice we don't have to do a string comparison to check if they are an instructor, e.g.: ```ruby > user.role == 'instructor' ``` ================================================ FILE: rails/make-action-mailer-synchronous-in-test.md ================================================ # Make ActionMailer Synchronous In Test When you set up an `ActionMailer` email, the default configuration is for it to use `ActiveJob` to send the emails. [As of Rails 5, it will do so asynchronously.](https://blog.bigbinary.com/2016/03/29/rails-5-changed-default-active-job-adapter-to-async.html). Depending on your preferences for testing emails, you may prefer `ActiveJob` to send the emails synchronously. This can be done by changing the `queue_adapter` back to `:inline` in your `config/environments/test.rb`. ```ruby config.active_job.queue_adapter = :inline ``` If you also configure the `delivery_method` as `:test`: ```ruby config.action_mailer.delivery_method = :test ``` then emails will be queued up in `ActionMailer::Base.deliveries` allowing you to write a test like this: ```ruby expect(ActionMailer::Base.deliveries.count).to eq(1) ``` Check out [the docs](https://guides.rubyonrails.org/action_mailer_basics.html) for more on `ActionMailer`. [source](https://stackoverflow.com/a/42987726/535590) ================================================ FILE: rails/make-remove-column-migration-reversible.md ================================================ # Make Remove Column Migration Reversible The Rails migration DSL includes a [`#remove_column`](https://edgeapi.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/SchemaStatements.html#method-i-remove_column) method for removing a column from a table. The two required values are the table name and the column name. ```ruby class RemoveNotesColumnFromTodo < ActiveRecord::Migration[5.2] def change remove_column :todos, :notes end end ``` This on it's own is not reversible though. If you were to run a `db:rollback` with this migration, Rails wouldn't know what type to use when re-adding the column. To make it reversible, you need to add in the column type as a third argument. ```ruby remove_column :todos, :notes, :string ``` Note: perhaps more importantly, this column isn't reversible in the sense that when you remove the column, you are throwing away all the data associated with that column. Be sure you're ready to part with this data before deploying a migration like this. [source](https://stackoverflow.com/questions/24520550/how-do-you-make-remove-column-reversible) ================================================ FILE: rails/manage-timestamps-with-upsert.md ================================================ # Manage Timestamps With Upsert Modern versions of Rails and ActiveRecord have [an `upsert` method](https://api.rubyonrails.org/v8.0.2/classes/ActiveRecord/Relation.html#method-i-upsert) which will, if available, use your database's upsert capability to either insert a new row or update an existing row based on the unique identifier. The docs have the following disclaimer: > It does not instantiate any models nor does it trigger Active Record > callbacks or validations. Though passed values go through Active Record’s > type casting and serialization. It's a bit different to work with than other ActiveRecord methods. It left me wondering if it would handle timestamp management or if I would have to do that myself. Let's upsert a new record into `books`: ```ruby > Book.upsert({title: "Shogun", author: "James Clavell", added_by_id: 1, publication_date: Date.today}, unique_by: :id) => # Book.select(:id, :title, :created_at, :updated_at).last => # ``` Notice that the `created_at` and `updated_at` timestamps get set. Now, let's upsert the record (notice we're passing in the `id`) to update the title with an `ō`. ```ruby > Book.upsert({id: 12, title: "Shōgun", author: "James Clavell", added_by_id: 1, publication_date: Date.today}, unique_by: :id) => # Book.select(:id, :title, :created_at, :updated_at).last => # ``` Notice that the `updated_at` gets set to a time about 2 minutes later. Lastly let's look at the `record_timestamps` option. This is `nil` by default which means the underlying methods default kicks in which _is_ to record the timestamps. We can override that behavior by passing in `false`. ```ruby > Book.upsert({id: 12, title: "Shōgun, Part 2", author: "James Clavell", added_by_id: 1, publication_date: Date.today}, unique_by: :id, record_timestamps: false) => # Book.select(:id, :title, :created_at, :updated_at).last => # ``` Notice that the `updated_at` value doesn't change between this upsert and the previous upsert. ================================================ FILE: rails/manually-run-a-migration-from-rails-console.md ================================================ # Manually Run A Migrations From Rails Console A migration can be manually run from the rails console. In 99% of cases you are going to be better off using the migration CLI that Rails provides (e.g. `rails db:migrate`, `rails db:rollback`, etc.). If you are in a hyper-specific scenario where you need to run the `up` or the `down` of a migration without the migration-table check, then you'll want to consider this approach. First, connect to the rails console: `rails c`. Then require your migration file. ```ruby > require "./db/migration/20200220181733_some_migration.rb" #=> true ``` You'll now have access to the `SomeMigration` constant. Create an instance of this and then run either the `up`-side of the migration: ```ruby > SomeMigration.new.up #=> ... # a bunch of migration output ``` or the `down`-side of it: ```ruby > SomeMigration.new.down #=> ... # a bunch of migration output ``` [source](https://stackoverflow.com/a/754316/535590) ================================================ FILE: rails/mark-a-migration-as-irreversible.md ================================================ # Mark A Migration As Irreversible It is in your best interest to, as much as is possible, write your Rails migrations in a way that they can be safely and reliably rolledback. You want your `down` to mirror your `up`, in case anything goes wrong. This isn't always possible though. There are some migrations, in particular data migrations, that cannot be undone. Something is being changed or destroyed in an unrecoverable way. When this is the case, you should, by convention, raise an `IrreversibleMigration` exception. ```ruby class DestructiveMigration < ActiveRecord::Migration[5.2] def up execute "-- some destructive SQL" end def down raise ActiveRecord::IrreversibleMigration end end ``` If anyone ever tries to rollback this migration, they will see the exception. It will be a signal that some manual work is needed to continue rolling back. See the [docs](https://api.rubyonrails.org/classes/ActiveRecord/Migration.html#class-ActiveRecord::Migration-label-Irreversible+transformations) for more details. ================================================ FILE: rails/mark-for-destruction.md ================================================ # Mark For Destruction Do you have some complicated logic or criteria for deleting associated records? [ActiveRecord's `#mark_for_destruction`](http://api.rubyonrails.org/classes/ActiveRecord/AutosaveAssociation.html#method-i-mark_for_destruction) may come in handy. Let's say we have _users_ who author _articles_. We want to delete some of the user's articles based on some criteria -- those articles that have odd `id`s. ```ruby > user = User.first #=> # > user.articles.each { |a| a.mark_for_destruction if a.id.odd? } #=> [#, ...] > user.articles.find(1).marked_for_destruction? #=> true > user.articles.find(2).marked_for_destruction? #=> false ``` We've marked our articles for destruction and confirmed as much with the [`#marked_for_destruction?`](http://api.rubyonrails.org/classes/ActiveRecord/AutosaveAssociation.html#method-i-marked_for_destruction-3F) method. Now, to go through with the destruction, we just have to save the parent record -- the user. ```ruby > user.save (0.2ms) BEGIN User Exists (0.8ms) SELECT 1 AS one FROM "users" WHERE ("users"."email" = 'person1@example.com' AND "users"."id" != 1) LIMIT 1 SQL (3.0ms) DELETE FROM "articles" WHERE "articles"."id" = $1 [["id", 1]] SQL (0.2ms) DELETE FROM "articles" WHERE "articles"."id" = $1 [["id", 3]] (2.1ms) COMMIT => true ``` Note: the parent record must have `autosave: true` declared on the association. ```ruby class User < ActiveRecord::Base has_many :articles, autosave: true end ``` ================================================ FILE: rails/mask-an-activerecord-attribute.md ================================================ # Mask An ActiveRecord Attribute Let's say we have a `User` model with backing table that has an `email` attribute. If we look up a `User` record, we can grab its email because Rails provides an accessor to that attribute under the hood. ```ruby > user.email 'Liz.Lemon@example.com' ``` We can write a custom `#email` method on `User` to mask that attribute. We could do this for any number of reasons. One might be to always downcase the email before retrieving it and using it througout app code. ```ruby class User < ApplicationRecord def email read_attribute(:email).downcase end end ``` This uses the [`#read_attribute`](https://api.rubyonrails.org/classes/ActiveRecord/AttributeMethods/Read.html#method-i-read_attribute) method which retreives the type casted value before hitting the accessor method. We have to do this, rather than calling `#email` directly, because that would result in an infinite loop. ```ruby > user.email 'liz.lemon@example.com' ``` I'd probably handle this email scenario at the DB-layer. Nevertheless, this demonstrates a technique we can use in a variety of scenarios at the Rails-layer. ================================================ FILE: rails/merge-a-scope-into-an-activerecord-query.md ================================================ # Merge A Scope Into An ActiveRecord Query Consider an ActiveRecord model with a scope: ```ruby class Book < ApplicationRecord scope :published, -> { where("books.published_at is not null") } end ``` Now let's say we are working in another part of the codebase composing a query that gathers all authors with published books. That might look something like this: ```ruby published_authors = Authors.joins(:book).where("books.published_at is not null") ``` This will get the job done, but we've now duplicated the same logic in different parts of the app. We can utilize the existing scope on `Book` using ActiveRecord's [`merge`](https://devdocs.io/rails~5.2/activerecord/spawnmethods#method-i-merge) method. ```ruby published_authors = Authors.joins(:book).merge( Book.published ) ``` The `merge` method can be used to incorporate any conditions from other partial queries -- this means both `where` clauses and `joins` clauses. [source](http://aokolish.me/blog/2015/05/26/how-to-simplify-active-record-scopes-that-reference-other-tables/) ================================================ FILE: rails/migrating-up-down-up.md ================================================ # Migrating Up Down Up When writing Rails migrations, it is good to define, when possible, what should happen when migrating *up* and what should happen when migrating *down*. You'll then want to check that both the *up* and *down* work. This can be accomplished using the following one-liner: ```bash $ rake db:migrate && rake db:migrate:redo ``` The `rake db:migration` does what we would expect applying our new migration and showing us that our *up* works. The `rake db:migrate:redo` first performs a rollback, showing us that our *down* works, and then migrates back up again. We now know that our latest migration works going both directions. ================================================ FILE: rails/mock-rails-environment-with-an-inquiry-instance.md ================================================ # Mock Rails Environment With An Inquiry Instance As discussed in [Make A String Attribute Easy to Inquire About](make-a-string-attribute-easy-to-inquire-about.md), the `Rails.env` is assigned an instance of `ActiveSupport::StringInquirer`. This allows us to ask whether the current Rails environment is `#production?`, `#development?`, etc. With this in mind, we can have a test execute in a specific environment by mocking how `Rails.env` responds. Though the actual env for a test is going to be `test`, we can simulate a different environment with an RSpec `before` block like the following: ```ruby before do allow(Rails).to receive(:env) { "staging".inquiry } end ``` Or similarly, to simulate the `production` environment: ```ruby before do allow(Rails).to receive(:env) { "production".inquiry } end ``` The `#inquiry` being monkey-patched onto the `String` class gives you the willies, you could do the following instead: ```ruby before do allow(Rails).to receive(:env) do ActiveSupport::StringInquirer.new("production") end end ``` [source](https://stackoverflow.com/a/25134591/535590) ================================================ FILE: rails/order-matters-for-rescue-from-blocks.md ================================================ # Order Matters For `rescue_from` Blocks In a Rails controller, you can declare any number of [`rescue_from` blocks](https://api.rubyonrails.org/classes/ActiveSupport/Rescuable/ClassMethods.html) for capturing and responding to execeptions that are raised by your application. ```ruby class BooksController < BaseController rescue_from ForbiddenAction do |e| render json: { error: e.message }.to_json, status: 403 end rescue_from StandardError do |e| render json: { error: e.message }.to_json, status: 500 end def index # ... raise ForbiddenAction, "Which rescue_from is this going to hit?" end end ``` The potential problem with above is the ordering of the two `rescue_from` blocks. Assume that `ForbiddenAction` is a subclass of the `StandardError` class -- this is likely the case for exceptions you declare in your app. The top `rescue_from` will never get hit because everything that subclasses `StandardError` will be trapped by the bottom `rescue_from`. These `rescue_from` blocks are applied bottom-up. That means you have to consider the class hierarchy when structuring your code. In the above code example, if we flip the two of them around, we will then get what we are expecting. ================================================ FILE: rails/override-text-displayed-by-form-label.md ================================================ # Override Text Displayed By Form Label Rails does a good job with the default text displayed by a form label. It takes the primary symbol value you give it and capitalizes that. And that is often good enough. ```ruby <%= form_with(model: post) do |form| %> <%= form.label :title, class: "text-sm font-medium text-gray-700" %> <%= form.text_field :title, required: true, class: "..." %> <% end %> ``` This will yield a label value of _Title_. Sometimes, however, the casing needs to be different or you need entirely different text. Take this URL field for example. Rails will convert `:url` into _Url_ for the label text. Not ideal. I can override the default with a second positional argument, in this case, `"URL"`. ```ruby <%= form_with(model: post) do |form| %> <%= form.label :url, "URL", class: "text-sm font-medium text-gray-700" %> <%= form.url_field :url, required: true, class: "..." %> <% end %> ``` The [Rails docs have another good example](https://guides.rubyonrails.org/form_helpers.html#a-generic-search-form). A label with a value of `query` that is overridden to display "Search for:". ```ruby <%= form_with url: "/search", method: :get do |form| %> <%= form.label :query, "Search for:" %> <%= form.search_field :query %> <%= form.submit "Search" %> <% end %> ``` ================================================ FILE: rails/parameterize-a-string-with-underscores.md ================================================ # Parameterize A String With Underscores I have human-readable status strings that I'm working with like `In progress`, `Pending approval`, and `Completed`. I need to deterministically turn those into parameterized values that I can compare. That is, I want them lowercased and separated by underscores instead of spaces. The `ActiveSupport` `#parameterize` method, as is, gets me pretty close. ```ruby > statuses = [ "In progress", "Pending approval", "Completed" ] > statuses.map(&:parameterize) => [ "in-progress", "pending-approval", "completed" ] ``` Those are separated by dashes though. Fortunately, `parameterize` takes a `separator` option that we can use to verride what character is used to separate words. Let's use an underscore (`_`). ```ruby > statuses.map { |str| str.parameterize(separator: '_') } => [ "in_progress", "pending_approval", "completed" ] ``` See the [`#paramterize` docs](https://api.rubyonrails.org/classes/ActiveSupport/Inflector.html#method-i-parameterize) for more details. ================================================ FILE: rails/params-includes-submission-button-info.md ================================================ # Params Includes Submission Button Info When a form is submitted for a Rails app, the respective controller action will have access to a variety of information in the `params` hash. Included is an entry with the name and value of the button that submitted the form. By default, Rails will give the name `commit` to a submission button. ``` <%= f.submit %> # results in: Submit ``` The corresponding `create` action will have parameters that include that submission button's info: ```ruby # in create action > params['commit'] => 'Submit' ``` This is useful when you have multiple buttons that submit the same form, but should have slightly different results in the corresponding action. Differentiating becomes easy when you can easily check which was used to submit the form. No javascript required. ================================================ FILE: rails/params-is-a-hash-with-indifferent-access.md ================================================ # Params Is A Hash With Indifferent Access When you have an instance of `ActionController::Parameters`—which you often do with `params` in a Rails controller—under the hood you have a `HashWithIndifferentAccess`. Here is what the initializer looks like when you call `ActionController::Parameters.new(some_hash)`. ```ruby def initialize(parameters = {}, logging_context = {}) @parameters = parameters.with_indifferent_access @logging_context = logging_context @permitted = self.class.permit_all_parameters end ``` This means you can reference the keys in your parameters as either a _string_ key or a _symbol_ key. ```ruby > params = ActionController::Parameters.new({ username: 'tacocat' }) => #"tacocat"} permitted: false> > params['username'] => "tacocat" > params[:username] => "tacocat" ``` Note that `ActiveSupport::HashWithIndifferentAccess` is not an ancestor of the `ActionController::Parameters` class (like it once used to be), but is just how the incoming hash is transformed when initialized. ================================================ FILE: rails/parse-query-params-from-a-url.md ================================================ # Parse Query Params From A URL For all of the conveniences that Ruby and Rails affords a developer through their expansive APIs, I am always surprised that it is hard to inspect the query params in a URL. Let's take a URL and walk through the steps it takes to pull out the value of a query param. Here's a URL: ```ruby url = "https://example.com?taco=bell&taco_count=3" => "https://example.com?taco=bell&taco_count=3" ``` Let's parse the URL with `URI`: ```ruby > URI(url) => # ``` Then grab the `query` part of that `URI`: ```ruby > URI(url).query => "taco=bell&taco_count=3" ``` This is an unparsed string. In a Rails context, this can be parsed with `Rack::Utils.parse_nested_query`: ```ruby > query_params = Rack::Utils.parse_nested_query(URI(url).query) => {"taco"=>"bell", "taco_count"=>"3"} ``` And now we have a hash of values we can inspect: ```ruby > query_params["taco_count"] => "3" ``` Be sure to do _string_ and not _symbol_ hash access here. These steps can be wrapped up into a method: ```ruby module UrlHelpers def query_params(url) unparsed_query_params = URI(url).query Rack::Utils.parse_nested_query(unparsed_query_params) end end ``` [source](https://stackoverflow.com/a/3218018/535590) ================================================ FILE: rails/parse-request-params-in-rack-attack-block.md ================================================ # Parse Request Params In Rack::Attack Block The [`Rack::Attack` docs](https://github.com/rack/rack-attack) demonstrate a way of throttling requests based on a value in the request params. In this example, it is a Sign In endpoint and the `email` is the discriminating value. ```ruby Rack::Attack.throttle('limit logins per email', limit: 6, period: 60) do |req| if req.path == '/login' && req.post? # Normalize the email, using the same logic as your authentication process, to # protect against rate limit bypasses. req.params['email'].to_s.downcase.gsub(/\s+/, "") end end ``` Depending on the particulars of your middleware, it may be the case that `req.params` is empty. That is because the request params need to be manually parsed from the body of the request. An updated example that parses the params before accessing them could look like this: ```ruby Rack::Attack.throttle('limit logins per email', limit: 6, period: 60) do |req| if req.path == '/login' && req.post? params = JSON.parse(req.body.string) # Normalize the email, using the same logic as your authentication process, to # protect against rate limit bypasses. params['email'].to_s.downcase.gsub(/\s+/, "") end end ``` You can pry into the block or add some logging to ensure that you are getting at the POST params you are interested in. [source](https://github.com/rack/rack-attack/issues/189#issuecomment-744593703) ================================================ FILE: rails/perform-sql-explain-with-activerecord.md ================================================ # Perform SQL Explain With ActiveRecord Want to check out the performance characteristics of some SQL query from within a Pry session? `ActiveRecord` allows you to perform a SQL `explain` on any `ActiveRecord::Relation` object. After chaining some Arel functions together, add an `#explain`. Here is an example: ```ruby Recipe.all.joins(:ingredient_amounts).explain Recipe Load (0.9ms) SELECT "recipes".* FROM "recipes" INNER JOIN "ingredient_amounts" ON "ingredient_amounts"."recipe_id" = "recipes"."id" => EXPLAIN for: SELECT "recipes".* FROM "recipes" INNER JOIN "ingredient_amounts" ON "ingredient_amounts"."recipe_id" = "recipes"."id" QUERY PLAN ---------------------------------------------------------------------------- Hash Join (cost=1.09..26.43 rows=22 width=148) Hash Cond: (ingredient_amounts.recipe_id = recipes.id) -> Seq Scan on ingredient_amounts (cost=0.00..21.00 rows=1100 width=4) -> Hash (cost=1.04..1.04 rows=4 width=148) -> Seq Scan on recipes (cost=0.00..1.04 rows=4 width=148) (5 rows) ``` [source](https://robots.thoughtbot.com/why-postgres-wont-always-use-an-index) ================================================ FILE: rails/polymorphic-path-helpers.md ================================================ # Polymorphic Path Helpers Underlying many of the path helpers that we use day to day when building out the views in our Rails apps are a set of methods in the [`ActionDispatch::Routing::PolymorphicRoutes`](http://api.rubyonrails.org/classes/ActionDispatch/Routing/PolymorphicRoutes.html) module. The `#polymorphic_path` method given an instance of a model will produce the relevant show path. ```ruby > app.polymorphic_path(Article.first) Article Load (0.5ms) SELECT "articles".* FROM "articles" ORDER BY "articles"."id" ASC LIMIT 1 => "/articles/2" ``` Given just the model's constant, it will produce the index path. ```ruby > app.polymorphic_path(Article) => "/articles" ``` Additionally, there are variants with `edit_` and `new_` prefixed for generating the edit and new paths respectively. ```ruby > app.edit_polymorphic_path(Article.first) Article Load (0.6ms) SELECT "articles".* FROM "articles" ORDER BY "articles"."id" ASC LIMIT 1 => "/articles/2/edit" > app.new_polymorphic_path(Article) => "/articles/new" ``` ================================================ FILE: rails/prefer-select-all-over-execute-for-read-queries.md ================================================ # Prefer select_all Over execute For Read Queries Though the `#execute` function provided by ActiveRecord technically works as a general-purpose query runner for strings of raw SQL, it has some downsides. First, let's say we have a large semi-complex (better in SQL than ActiveRecord DSL) SQL query defined in a heredoc. ```ruby books_by_status_query = <<-SQL select books.*, latest_statuses.status as current_status, array_to_json(array_agg(...)) as reading_statuses from books -- plus several left joins -- where clause, group by, and order by SQL ``` I reflexively reach for [`#execute`](https://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/DatabaseStatements.html#method-i-execute) in a situation like that: ```ruby result = ActiveRecord::Base.connection.execute(books_by_status_query) ``` However, if we're doing a read-only query and we are expecting multiple rows in the result, then we are better off reaching for [`#select_all`](https://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/DatabaseStatements.html#method-i-select_all). ```ruby result = ActiveRecord::Base.connection.select_all(books_by_status_query) ``` It has the advantage of semantically communicating that it's just a read and won't have any side-effects. It also avoids an unnecessary clear of the query cache. > Note: [when execute is used] the query is assumed to have side effects and > the query cache will be cleared. If the query is read-only, consider using > select_all instead. The `#execute` method also has been known to leak memory with some database connectors. > Note: depending on your database connector, the result returned by this > method may be manually memory managed. Consider using exec_query wrapper > instead. We can then iterate through and transform the results just as we would have done with `#execute`. ```ruby result.map do |row| row.tap do |hash| hash["reading_statuses"] = JSON.parse(hash["reading_statuses"]) end OpenStruct.new(row) end ``` ================================================ FILE: rails/pretend-generations.md ================================================ # Pretend Generations To get an idea of what a `rails generate` command is going to to *generate*, you can do a dry run with the `-p` flag or the `--pretend` flag. If you run ``` $ rails generate model post -p ``` then you will see the following output ``` invoke active_record create db/migrate/20150513132556_create_posts.rb create app/models/post.rb invoke rspec create spec/models/post_spec.rb invoke factory_girl create spec/factories/posts.rb ``` though those files will not have actually been created. You now know precisely what rails will generate for you. [source](http://rubyquicktips.com/post/19223887750/pretend-to-generate) ================================================ FILE: rails/prevent-mailer-previews-from-cluttering-database.md ================================================ # Prevent Mailer Previews From Cluttering Database ActionMailer Previews give you a way to view email templates that your system sends. This is how I check that it is styled properly and that the logic and data of the template are able to run and render. Data for a preview typically means we need ActiveRecord objects and even their associations. If we start creating one-off records in our previews either with `#create` or with something like `FactoryBot`, those records will get left behind in our development database. Every view and refresh of a preview will generate more of these records. One way to get around that is to use `#new` and `#build`. I've found this cumbersome and it often leaves assocations missing or inaccessible. What if instead the preview could clean up after itself? That sounds like a great job for a database transaction. Let's create a `test/mailers/previews/base_preview.rb` as a base class for all our preview classes. ```ruby class BasePreview < ActionMailer::Preview def self.call(...) message = nil ActiveRecord::Base.transaction do message = super(...) raise ActiveRecord::Rollback end message end end ``` This wraps the existing `self.call` functionality in a transaction that collects the resulting message from the preview and then rolls back the database changes. Now, instead of our individual preview classes inheriting directly from `ActionMailer::Preview`, they can inherit from `BasePreview`. ```ruby class UserMailer < BasePreview # ... end ``` [source](https://stackoverflow.com/a/31289295) ================================================ FILE: rails/prevent-writes-with-a-sandboxed-rails-console.md ================================================ # Prevent Writes With A Sandboxed Rails Console I often open a `rails console` to play around with some data and make sure I understand how some models can be instantiated while respecting their associations. There are plenty of times where I've created some data in the `development` database that doesn't need to be there. It may even be incomplete data from a failed experiment. This data accumlates and clutters up the database. One way to avoid this is by running the console in a sandboxed mode. Include the `--sandbox` flag when starting up a session to do this. ```bash $ rails console --sandbox Loading development environment in sandbox (Rails 5.2.6) Any modifications you make will be rolled back on exit [1] pry(main)> ``` This wraps the session in a transaction so that any writes to the database can be rolledback afterward. [source](https://dev.to/citizen428/rails-quick-tips-1-console-sandbox-4k0c) ================================================ FILE: rails/provide-fake-form-helper-to-controllers.md ================================================ # Provide Fake Form Helper To Controllers I'm rendering a partial from a turbo stream. The partial is meant to be rendered within a Rails form object because it contains an input element that needs to reference the form object. The problem is that from the controller that is streaming the partial, there is no [FormBuilder](https://api.rubyonrails.org/v6.1.0/classes/ActionView/Helpers/FormBuilder.html) object. One way to get around this that I've borrowed from [Justin Searls](https://justin.searls.co/posts/instantiate-a-custom-rails-formbuilder-without-using-form_with/) is with a `FauxFormHelper`. ```ruby module FauxFormHelper FauxFormObject = Struct.new do def errors end def method_missing(...) end def respond_to_missing?(...) true end end def faux_form @faux_form ||= ActionView::Helpers::FormBuilder.new( nil, FauxFormObject.new, self, {} ) end end ``` This module defines and exposes a `faux_form` object that controllers and views can access. Then my partial can recieve that form object as a parameter. ================================================ FILE: rails/query-a-single-value-from-the-database.md ================================================ # Query A Single Value From The Database In a Rails context, most database interactions tend to happen through the ORM (e.g. `Book.find("123")`). There is a general purpose escape hatch that lets you execute a SQL statement directly against the DB -- `execute`. The resulting value of `execute`, however, tends to be a little clunky to work with. If you just need a single value from the DB, use the [`select_value`](https://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/DatabaseStatements.html#method-i-select_value) method. ```ruby > statement = "select gen_random_uuid()" > ActiveRecord::Base.connection.select_value(statement) (5.0ms) select gen_random_uuid() => "abc2e780-f442-418b-afa3-56f0ccd0a903" ``` This is the cleanest way to get the result of a "single value" query. If you happen to pass in a query that results in more than one row or column, it will return the value of the first column from the first row. ================================================ FILE: rails/read-in-environment-specific-config-values.md ================================================ # Read In Environment-Specific Config Values The [`config_for`](https://apidock.com/rails/Rails/Application/config_for) method allows you to read in a YAML file for the current environment in your config files. These YAML files need to be located in the `config` directory. For instance, let's say you have `config/extra_settings.yml`: ```yml development: support_email: support+dev@test.com production: support_email: support@test.com ``` You can read that file in as part of your `application.rb` config setup: ```ruby # config/application.rb module MyApp class Application < Rails::Application extra_settings = config_for(:extra_settings) set_support_email(extra_settings.fetch(:support_email)) end end ``` In `development` this call to `config_for` would return a hash of the values under `development`. Likewise in `production`. ================================================ FILE: rails/read-only-models.md ================================================ # Read-Only Models Are you in the midst of a big refactoring that is phasing out an `ActiveRecord` model? You may not be ready to wipe it from the project, but you don't want it accidentally used to create any database records. You essentially want your model to be read-only until it is time to actually delete it. This can be achieved by adding a `readonly?` method to that model that always returns `true`. ```ruby def readonly? true end ``` `ActiveRecord`'s underlying persistence methods always check `readonly?` before creating or updating any records. [source](http://stackoverflow.com/questions/5641410/is-there-an-easy-way-to-make-a-rails-activerecord-model-read-only) h/t Josh Davey ================================================ FILE: rails/rebuild-tailwind-bundle-for-dev-server.md ================================================ # Rebuild Tailwind Bundle For Dev Server If you're using the TailwindCSS gem in your Rails app: ```ruby # Use Tailwind CSS [https://github.com/rails/tailwindcss-rails] gem "tailwindcss-rails" ``` you may find that as you add and adjust styles in your views, refreshing the page doesn't take any styling effects. That is because the tailwind bundle gets built with just the style rules that were used at the time it was generated. In development, as we're working, we expect the styles used by our app to actively changed. And we don't mind a little performance hit to have the bundle rebuilt. In that case, we can instruct `puma` to _Live Rebuild_ in `development` with the `tailwindcss` plugin. ```ruby # config/puma.rb # Enable TailwindCSS rebuild in development plugin :tailwindcss if ENV.fetch("RAILS_ENV", "development") == "development" ``` This has `rails server` run a watch process in the background that live rebuilds the bundle. [source](https://github.com/rails/tailwindcss-rails?tab=readme-ov-file#puma-plugin) ================================================ FILE: rails/remove-a-database-column-from-a-table.md ================================================ # Remove A Database Column From A Table The `ActiveRecord` migration DSL includes a method [`remove_column`](https://api.rubyonrails.org/v7.0.3.1/classes/ActiveRecord/ConnectionAdapters/SchemaStatements.html#method-i-remove_column) that can be used to remove an existing column from a table. It can be used like so, to remove the `sign_in_count` column from the `users` table. ```ruby def change remove_column :users, :sign_in_count end ``` Though that will work fine, you'll run into an `IrreversibleMigration` error if you try to `rails db:rollback`. It usually a good bet to make migrations reversible when it is easy to do so. All we need in order to make this migration reversible is to add the column type. ```ruby def change remove_column :users, :sign_in_count, :integer end ``` Now you can rollback (or [migrate up-down-up](migrating-up-down-up.md)) no problem. Keep in mind that only the structural changes are reversible. When you remove the column, all of the data goes with it, and that cannot be undone with a simple rollback. ================================================ FILE: rails/remove-the-default-value-on-a-column.md ================================================ # Remove The Default Value On A Column You have a column on one of your database tables with a default value. You'd like to remove the default value. Removing the default is the same as setting it to `nil`. You can do this with the ActiveRecord DSL using the `change_column_default` method. ```ruby def change change_column_default :users, :age, nil end ``` See [the docs](http://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/SchemaStatements.html#method-i-change_column_default) for more details. ================================================ FILE: rails/render-an-alternative-action-mailer-template.md ================================================ # Render An Alternative ActionMailer Template The convention for Rails's ActionMailer is to render the view template that corresponds to the mailer class's name and the specific action. For instance, the `welcome_email` method in the `RegistrationMailer` class will correspond to the `app/views/registration_mailer/welcome_email.html.erb` view. This convention can be overridden. By passing, the `template_name` and `template_path` arguments as options to the `mail` method, you can tell the mailer to render a different template. ```ruby class RegistrationMailer < ActionMailer::Base def welcome_email # mail setup ... mail(to: @user.email, subject: 'Welcome!', template_name: 'new_welcome') end end ``` This will look for and use the `app/views/registration_mailer/new_welcome.html.erb` template. Also including the `template_path` option will alter the path to the named template: ```ruby class RegistrationMailer < ActionMailer::Base def welcome_email # mail setup ... mail(to: @user.email, subject: 'Welcome!', template_path: 'v2_mailer_templates', template_name: 'new_welcome') end end ``` This will look for the `app/views/v2_mailer_templates/new_welcome.html.erb` template. [source](https://guides.rubyonrails.org/action_mailer_basics.html#mailer-views) ================================================ FILE: rails/render-the-response-body-in-controller-specs.md ================================================ # Render The Response Body In Controller Specs Controller specs skip the rendering of views by default. If you want to inspect some aspect of what is rendered in the HTML body of a response (`response.body`), you can include the `render_views` directive in that spec. ```ruby require 'rails_helper' RSpec.describe DashboardController do describe '#index' do render_views context 'when there is a signed in user' do it 'includes their email' do user = User.create(email: 'user@example.com') sign_in(user) get :index expect(response.body).to include('user@example.com') end end end end ``` The `render_views` directive call can go at the top of a spec, and all views for all tests will be rendered. Or you can place it in the nested contexts only where it is needed. View rendering is skipped by default in an effort to keep tests speedy. To not unnecessarily slow down your test suite, make sure to use it sparingly and only in tests where you are actually inspecting `response.body`. ================================================ FILE: rails/replace-an-index-with-a-unique-index.md ================================================ # Replace An Index With A Unique Index Indexes and uniqueness constraints often go together. In fact, in Postgres, when you create a unique constraint, an index is created under the hood to support that constraint. What if you already have an index, but you want to turn it into a unique index? There is no way to alter or update the index to be unique. Instead, what you'll want to do is drop the index and then recreate it as a unique index. Here's how you can do that with the Rails migration DSL: ```ruby class ReplaceIndexWithUniqueIndex < ActiveRecord::Migration[5.2] disable_ddl_transaction! def up remove_index :users_roles, [:user_id, :role_id] add_index :users_roles, [:user_id, :role_id], unique: true, algorithm: :concurrently end def down remove_index :users_roles, [:user_id, :role_id] add_index :users_roles, [:user_id, :role_id], algorithm: :concurrently end end ``` This removes the original multi-column index and then adds back in a unique index that covers the same columns. I added `disable_ddl_transactions!` so that the new index could be added concurrently. I've also included a `down` migration that reverses the process in case a rollback is needed. ================================================ FILE: rails/rescue-from-with-a-separate-method.md ================================================ # Rescue From With A Separate Method In an earlier post on [`rescue_from`](rescue_from.md), I showed how you can write an exception handler with a block argument. If instead you'd prefer to implement the exception handler as a separate method, you can do that by passing [`rescue_from`](http://api.rubyonrails.org/classes/ActiveSupport/Rescuable/ClassMethods.html) a `with` keyword argument. This too will behave as a catch-all for a particular exception raised in your controller. Here is what that can look like: ```ruby class ApplicationController < ActionController::Base rescue_from User::NotAuthorized, with: :handle_unauthorized_user def index # ... end private def handle_unauthorized_user(exception) # respond with some Not Authorized page end end ``` If the `User::NotAuthorized` exception bubbles up to the `rescue_from`, then it will hand that exception off to the `handle_unauthorized_user` method. We can include whatever logic and monitoring we want here and then render an appropriate response. ================================================ FILE: rails/rescue-from.md ================================================ # Rescue From The [`rescue_from`](http://api.rubyonrails.org/classes/ActiveSupport/Rescuable/ClassMethods.html) method, provided by `ActiveSupport`, is a handy way to provide a catch-all response to a particular exception in Rails controllers. For instance, if many of the controllers in your application raise a `User::NotAuthorized` error for unauthorized requests, the `ApplicationController` can provide a unified response. This will help dry up your controllers and prevent any potential inconsistencies. ```ruby class ApplicationController < ActionController::Base rescue_from User::NotAuthorized do |exception| # respond with some Not Authorized page end ... end ``` ================================================ FILE: rails/respond-with-json-regardless-of-content-type.md ================================================ # Respond With JSON Regardless Of Content Type Let's say you want to serve some JSON from an endpoint (for example, you might be serving [the `apple-app-site-association` file for iOS Universal Links](https://developer.apple.com/documentation/xcode/supporting-associated-domains)). Regardless of whether the endpoint is requested as JSON (`application/json`), HTML (`text/html`), or something else (`plain/text`), you want to respond with JSON. The [`format#any` method](https://api.rubyonrails.org/classes/ActionController/MimeResponds.html) can be used when defining the `respond_to` block. This tells the controller that _any_ mimetype is accepted. ```ruby def show respond_to do |format| format.any do render params[:page], formats: 'json', content_type: "application/json", layout: false end end end ``` The other important element in this is `formats: 'json'` which helps Rails find your `.json.erb` file in the `views` directory. Though I cannot find any documentation for it, `format.all` appears to work the same as `format.any` as described in the above example. h/t Dillon Hafer ================================================ FILE: rails/restart-puma-server-by-touching-restart-file.md ================================================ # Restart Puma Server By Touching Restart File Puma includes a plugin that allows us to restart the web server by touching the `tmp/restart.txt` file. In one terminal pane I have my Rails server running. In another terminal pane from the Rails directory, where there exists a `tmp` folder, I run the following command. ```bash $ touch tmp/restart.txt ``` Then in the pane running the Rails server, I see the following after a second: ``` * Restarting... => Booting Puma => Rails 8.0.1 application starting in development ... ``` What is happening is that `touch` updates the modified time of that file, which already exists in the `temp` directory. When the plugin notices (it checks every 2 seconds) that the modified time is now fresher than the original modified time when the plugin started, then it calls `launcher.restart`. [source](https://github.com/puma/puma/blob/ca201ef69757f8830b636251b0af7a51270eb68a/lib/puma/plugin/tmp_restart.rb) ================================================ FILE: rails/retrieve-an-object-if-it-exists.md ================================================ # Retrieve An Object If It Exists Rails' Active Support provides the `blank?` and `present?` convenience methods as extensions to many objects. It also extends the Object class by providing the [`presence`](http://api.rubyonrails.org/classes/Object.html#method-i-presence) method. This method returns the *receiver* if it is not blank, otherwise it returns nil. Instead of doing ```ruby User.nickname.present? ? User.nickname : User.firstname ``` I can simply do ```ruby User.nickname.presence || User.firstname ``` ================================================ FILE: rails/rollback-a-couple-migrations.md ================================================ # Rollback A Couple Migrations Let's say we need to rollback a couple Rails migrations that have been applied to our local environment. We run `rails db:migrate:status` and see that there are _2_ migrations that we want to _undo_. We can accomplish this by using the `STEP` env var with the rollback command. ```bash $ rails db:rollback STEP=2 ``` Just set `STEP` to the number of migrations that we need to rollback. If we then rerun `rails db:migrate:status` we'll now see those latest two migrations are `down`. Note: by default Rails doesn't like to operate with pending migrations. If we want to temporarily disable the pending migration check, we can alter the migration error config in `config/development.rb`. ```diff # Raise an error on page load if there are pending migrations. - # config.active_record.migration_error = :page_load + config.active_record.migration_error = false ``` ================================================ FILE: rails/rollback-a-specific-migration-out-of-order.md ================================================ # Rollback A Specific Migration Out Of Order If you want to rollback the latest migration that is `up`, you can use: ```bash $ rails db:rollback ``` It deals explicitly with the latest `up` migration and nothing else. Even if you name a specific `VERSION`, it will still just rollback the one latest. Instead, if you want to target a specific past migration for rollback, you'll want the `db:migrate:down` command. You'll need to specify the version, which is the timestamp number in the filename of the migration. Here is what this looks like for a recent migration I had to rollback. ```bash $ rails db:migrate:down VERSION=20210302171858 ``` [source](https://stackoverflow.com/a/3647820/535590) ================================================ FILE: rails/rounding-numbers-with-precision.md ================================================ # Rounding Numbers With Precision Ruby's `Float#round` method gets the job done, but doesn't offer much configurability. If you'd like to finely control how a rounded number will display, `ActiveSupport::NumberHelper` offers [`number_to_rounded`](https://api.rubyonrails.org/classes/ActiveSupport/NumberHelper.html#method-i-number_to_rounded). When a precision is specified, it will apply to the fraction digits: ```ruby > ActiveSupport::NumberHelper.number_to_rounded(1.0, precision: 2) => "1.00" ``` Unless you include `significant: true` in which case precision will refer to the number of signficant digits: ```ruby > ActiveSupport::NumberHelper.number_to_rounded(1.0, precision: 2, significant: true) => "1.0" ``` Because this is for display purposes, the return value is a string. You can further specify that insignificant zeros are stripped from the result: ```ruby > ActiveSupport::NumberHelper.number_to_rounded(1.0, precision: 2, significant: true, strip_insignificant_zeros: true) => "1" ``` And for completeness, here is an example of a number being rounded up: ```ruby > ActiveSupport::NumberHelper.number_to_rounded(1.29, precision: 2, significant: true) => "1.3" ``` [source](https://api.rubyonrails.org/classes/ActiveSupport/NumberHelper.html#method-i-number_to_rounded) ================================================ FILE: rails/run-a-rake-task-programmatically.md ================================================ # Run A Rake Task Programmatically Typically the way to run a rake task is with the `rake` command from the command line. ```bash $ rake example:env ``` What if you have a rake task that you want to invoke as part of a Ruby script or from somewhere in your Rails codebase? Your tasks can be called programmatically as well. Consider these two rake tasks: ```ruby namespace :example do task :env do puts "Current Environment: #{Rails.env.upcase}" end task :message, [:msg] do |task, args| puts "Message: #{args[:msg]}" end end ``` These can be called from somewhere else by referencing and invoking them like so. ```ruby Rake::Task["example:env"].invoke Rake::Task["example:message"].invoke("Nice rake task!") ``` ================================================ FILE: rails/run-commands-with-specific-rails-version.md ================================================ # Run Commands With Specific Rails Version You can have multiple versions of a gem like `rails` installed with `gem`. However, when you go to run a rails command, your system will default to using the latest version that you have installed. So doing a version check will show that version to currently be `7.1.3` and running something like `rails new` will set up a new Rails 7.1.3 app. ```bash $ rails --version Rails 7.1.3 $ rails new my_app ``` If you want to use a Rails version besides the latest you have installed for whatever command, you can use a `gem` convention which is to put `__` right after the gem name. Let's try this for Rails 6.1.3: ```bash $ rails _6.1.3_ --version Rails 6.1.3 $ rails _6.1.3_ new my_app ``` [source](https://stackoverflow.com/a/452458/535590) ================================================ FILE: rails/run-dev-processes-with-overmind-instead-of-foreman.md ================================================ # Run Dev Processes With Overmind Instead Of Foreman Most Rails projects that I have worked on have used [`foreman`](https://github.com/ddollar/foreman) as a development dependency for running all the processes declared in your Procfile (`Procfile.dev`). As far as having a single command to run everything (Rails server, asset building, worker(s), etc.), it does the job. `foreman` has some serious points of friction though. The one that really stands out to me is that when I try to debug the development Rails server with `binding.irb` or `binding.pry`, the other processes tend to interfere. The alternative to `foreman` that I've been trying out recently is [`overmind`](https://github.com/DarthSim/overmind). A specific selling point of `overmind` is that it runs all the development processes in a `tmux` session. That means you can individually connect to, inspect, and restart each process. Once you've installed `overmind` (`brew install overmind`), then you can easily swap it in for `foreman` like so: ```bash $ overmind start -f Procfile.dev ``` You can connect to any of those processes directly: ```bash $ overmind connect sidekiq ``` When you want to `binding.irb` the Rails server, you can specifically connect to the `web` process to do that. ```bash $ overmind connect web ``` If you need to stop all the process, you can run the `kill` subcommand. ```bash $ overmind kill ``` Lastly, if you have a `bin/dev` script in your project, it is probably using `foreman`. If you and your team prefer `overmind`, then update that script accordingly and you can simply run `bin/dev` going forward. ================================================ FILE: rails/run-rails-console-with-remote-dokku-app.md ================================================ # Run Rails Console With Remote Dokku App Whenever I want to `rails console` into the _staging_ server of an app I'm working on, I first have to `ssh` into server and then I have to come up with the [`dokku`](https://dokku.com/) command to run `rails console` against the app on that server. ```bash local> ssh app-staging # app-staging is an SSH alias staging> dokku run my-app rails console ``` I figured out how to reduce the friction of this by collapsing it into a single command that I can run locally. I can remotely run the `dokku` command with `ssh` using an interactive session (`-t`). ```bash local> ssh -t app-staging dokku run my-app rails console ``` That will open up a `rails console` session directly in the current shell session via a remote SSH connection. The `-t` flag is important because that makes the session interactive so that I can interact with the REPL. I've even packaged this up into a bin script (`bin/staging-console`) with a couple checks to enhance the DX. I won't put the whole thing here, but the gist of it is: ```bash #!/usr/bin/env bash set -e if [ -z "$DOKKU_STAGING_SSH_ALIAS" ]; then echo "Error: DOKKU_STAGING_SSH_ALIAS environment variable is not set." echo "" # echo more help details here ... exit 1 fi # Check if SSH alias exists # ... # Check if we can reach the server # ... # Run the console ssh -t "$DOKKU_STAGING_SSH_ALIAS" dokku run my-app rails console "$@" ``` ================================================ FILE: rails/run-some-code-whenever-rails-console-starts.md ================================================ # Run Some Code Whenever Rails Console Starts It can be handy to run some code whenever the `rails console` command is run. You may want to have some modules required, some variables set up, or, both fancy and practical, default to the read replica database in production. Rails provides a hook into the console startup with the `console` block in `config/application.rb`. Here is what it looks like to `puts` out the environment: ```ruby class Application < Rails::Application # everything else ... console do puts '############################################' puts 'Connected to the #{Rails.env.upcase} console' puts '############################################' end end ``` To avoid cluttering `config/application.rb` with a bunch of console-specific logic, you can move it to another file and then have the console block require that file with the `-r` flag. ```ruby class Application < Rails::Application console do ARGV.push "-r", Rails.root.join("lib/console.rb") end end ``` [source](https://til.hashrocket.com/posts/avb2v3ubdt-pass-a-block-on-console-load) ================================================ FILE: rails/scaffold-auth-functionality-with-rails-8-generator.md ================================================ # Scaffold Auth Functionality With Rails 8 Generator Rails 8 added a built-in generator for authentication that scaffolds the core models, controllers, views, routes, etc. needed for a basic email/password authentication flow. It creates a `User` model, if one doesn't already exist, as the authenticated object. It uses the `bcrypt` gem for password hashing, etc. Here is an example of what you get when running the generator on a relatively new Rails 8 project: ```bash $ bin/rails generate authentication invoke tailwindcss create app/views/passwords/new.html.erb create app/views/passwords/edit.html.erb create app/views/sessions/new.html.erb create app/models/session.rb create app/models/user.rb create app/models/current.rb create app/controllers/sessions_controller.rb create app/controllers/concerns/authentication.rb create app/controllers/passwords_controller.rb create app/channels/application_cable/connection.rb create app/mailers/passwords_mailer.rb create app/views/passwords_mailer/reset.html.erb create app/views/passwords_mailer/reset.text.erb create test/mailers/previews/passwords_mailer_preview.rb insert app/controllers/application_controller.rb route resources :passwords, param: :token route resource :session gsub Gemfile bundle install --quiet generate migration CreateUsers email_address:string!:uniq password_digest:string! --force rails generate migration CreateUsers email_address:string!:uniq password_digest:string! --force invoke active_record create db/migrate/20250115224625_create_users.rb generate migration CreateSessions user:references ip_address:string user_agent:string --force rails generate migration CreateSessions user:references ip_address:string user_agent:string --force invoke active_record create db/migrate/20250115224626_create_sessions.rb ``` [source](https://www.bigbinary.com/blog/rails-8-introduces-a-basic-authentication-generator) ================================================ FILE: rails/schedule-sidekiq-jobs-out-into-the-future.md ================================================ # Schedule Sidekiq Jobs Out Into The Future The most common way to schedule a [Sidekiq](https://github.com/mperham/sidekiq) job is with the `perform_async` method. That will queue up your job so that it is worked as soon as possible. That may not also be desired. Sometimes you want a bit more say in when jobs are run. The `perform_in` and `perform_at` methods can help with scheduling jobs out into the future. With `perform_in` we can say how much time from now would be the soonest that we'd like the job performed. ```ruby MyWorker.perform_in(10.minutes, arg1, arg2) ``` We can do the same thing with `perform_at`. ```ruby MyWorker.perform_at(10.minutes.from_now, arg1, arg2) ``` Or we can schedule something out for a specific point in time in the future. ```ruby MyWorker.perform_at(Date.today.end_of_week, arg1, arg2) ``` [source](https://github.com/mperham/sidekiq/wiki/Scheduled-Jobs) ================================================ FILE: rails/scope-records-to-a-lower-or-upper-bound.md ================================================ # Scope Records To A Lower Or Upper Bound Typically when we use [`#where`](https://api.rubyonrails.org/classes/ActiveRecord/QueryMethods.html#method-i-where) to scope queries against ActiveRecord models, we are looking to do a direct "equals" comparison. Such as `auth_codes.user_id = 1` in the example below. ```ruby > AuthCode.where(user_id: 1) AuthCode Load (0.4ms) SELECT "auth_codes".* FROM "auth_codes" WHERE "auth_codes"."user_id" = 1 /* loading for pp */ LIMIT 11 ``` We can do more powerful things with `#where` (assuming your database supports it, in my case PostgreSQL), such as comparing over ranges of dates. Ruby's range syntax gives us an elegant way to express ranges. ```ruby > 2..10 # range with lower bound of 2 and upper bound of 10 > 2.. # 'end'less range > ..10 # 'begin'less range ``` These latter two examples are ranges that are unbounded on one side or the other. We can use these in ActiveRecord `#where` queries to do "greater than or equal to" and "less than or equal to" conditionals. And we can do the same with ranges of dates like in the following queries. ```ruby > AuthCode.where(created_at: 10.days.ago..).count AuthCode Count (97.1ms) SELECT COUNT(*) FROM "auth_codes" WHERE "auth_codes"."created_at" >= '2025-09-24 00:35:46.937715' > AuthCode.where(created_at: 10.days.ago..5.days.ago).count AuthCode Count (0.6ms) SELECT COUNT(*) FROM "auth_codes" WHERE "auth_codes"."created_at" BETWEEN '2025-09-24 00:35:59.901441' AND '2025-09-29 00:35:59.901512' > AuthCode.where(created_at: ..5.days.ago).count AuthCode Count (0.3ms) SELECT COUNT(*) FROM "auth_codes" WHERE "auth_codes"."created_at" <= '2025-09-29 00:36:09.731444' ``` Notice in the generated SQL how the simple `#where` method gets transformed into a `>=`, a `<=`, or a `between` clause. And while dates are a powerful example of this, there is nothing to stop us from querying against other kinds of ranges like numeric ones. ```ruby # Orders under $10 ten_dollars_in_cents = 10 * 100 Order.where.not(fulfilled_at: nil).where(amount: ..ten_dollars_in_cents) ``` ================================================ FILE: rails/secure-passwords-with-rails-and-bcrypt.md ================================================ # Secure Passwords With Rails And Bcrypt If you are using [`bcrypt`](https://github.com/codahale/bcrypt-ruby) (at least version 3.1.7), then you can easily add secure password functionality to an [ActiveRecord](https://github.com/rails/rails/tree/master/activerecord) model. First, ensure that the table backing the model has a `password_digest` column. Then add [`has_secure_password`](https://api.rubyonrails.org/classes/ActiveModel/SecurePassword/ClassMethods.html) to your model. ```ruby class User < ActiveRecord::Base has_secure_password # other logic ... end ``` You can now instantiate a `User` instance with any required fields as well as `password` and `password_confirmation`. As long as `password` and `password_confirmation` match then an encrypted `password_digest` will be created and stored. You can later check a given password for the user using the `authenticate` method. ```ruby user = User.find_by(email: user_params[:email]) if user.authenticate(user_params[:password]) puts 'That is the correct password!' else puts 'That password did not match!' end ``` ================================================ FILE: rails/select-a-select-by-selector.md ================================================ # Select A Select By Selector Generally when using Capybara to select from a `select` input, I reference it by its `name` which rails associates with the label: ```ruby select("Charizard", from: "Pokemon") ``` However, not all forms are going to have a label paired with every `select` input. We don't want to let our test coverage suffer, so we are going to need a different way to select. Fortunately, Capybara allows us to chain `select` off a `find` like so: ```ruby find('#pokemon_list').select('Charizard') ``` ================================================ FILE: rails/select-a-specific-rails-version-to-install.md ================================================ # Select A Specific Rails Version To Install We can install a specific Rails version with `gem` using the version flag. ```bash $ gem install rails --version 7.1.3 ``` That's only if we already know which specific version we are intending to install. A better version of this would show us a list of available version and let us select the one we want. We can do this by fetching all remote Rails versions with `gem`, splitting that output up into a single version per line, and then piping that to an `fzf` prompt. The version we navigate to and select will be fed into the `gem install` command. ```bash gem install rails --version $( gem list rails --exact --remote --all \ | sed -n 's/.*(\([^)]*\)).*/\1/p' \ | tr ',' '\n' \ | sed 's/^ //' \ | fzf ) ``` ================================================ FILE: rails/select-value-for-sql-counts.md ================================================ # Select Value For SQL Counts If you are like me and prefer writing raw SQL over the Arel DSL for counting stuff in your database, then the `select_value` method will come in handy. Write a command similar to the following with a type cast to get the count of _whatever_. ```ruby > sql = 'select count(*) from posts where published_at is not null' => "select count(*) from posts where published_at is not null" > ActiveRecord::Base.connection.select_value(sql).to_i (0.6ms) select count(*) from posts where published_at is not null => 42 ``` Writing raw SQL for a simple query like this hardly seems like a win. However when a count query starts to involve joins or other fanciness, I find it much clearer to reason about the raw SQL. ================================================ FILE: rails/serialize-with-fast-jsonapi-in-a-rails-app.md ================================================ # Serialize With fast_jsonapi In A Rails App Netflix put out a Ruby gem for super fast JSON serialization -- [`fast_jsonapi`](https://github.com/Netflix/fast_jsonapi). It is great for serializing JSON responses for Rails API endpoints. First, add `gem 'fast_jsonapi'` to your `Gemfile` and `bundle install`. Then create the `app/serializers` directory for housing all of your JSON serializers. Next you can create a `serializer` that corresponds to the model you want to serialize: ```ruby # app/serializers/recipe_serializer.rb class RecipeSerializer include FastJsonapi::ObjectSerializer set_id :id attributes :name, :source_url end ``` Last, use it to generate a JSON response in your controller: ```ruby # app/controllers/recipes_controller.rb class RecipesController < ApiController def index render json: RecipeSerializer.new(@current_user.recipes) end end ``` Requests to that endpoint will receive a response that looks something like this: ```json { data: [ { id: 1, attributes: { name: "Old Fashioned", source_url: "http://..." }, }, { id: 2, attributes: { name: "Sazerac", source_url: "http://..." }, }, ] } ``` ================================================ FILE: rails/set-a-timestamp-field-to-the-current-time.md ================================================ # Set A Timestamp Field To The Current Time To set a timestamp field to the current time, you could reach for the `#update` method like you would when modifying any other field. ```ruby MagicLink .find_by(token: some_token) .update(used_at: Time.zone.now) ``` This works, but it's more verbose than is necessary and requires that you construct the right timestamp for _now_ (time zones and all). Rails has a more concise and idomatic way of doing this: [`#touch`](https://api.rubyonrails.org/v6.1.0/classes/ActiveRecord/Persistence.html#method-i-touch). ```ruby MagicLink .find_by(token: some_token) .touch(:used_at) ``` Updating a timestamp to the current time is a common action in web applications, so Rails offers the `#touch` method as a shorthand for doing it. This will set the given field, in this case `:used_at`, to the current time. This will also set the `updated_at/on` field. ================================================ FILE: rails/set-datetime-to-include-time-zone-in-migrations.md ================================================ # Set Datetime To Include Time Zone In Migrations When using Rails and PostgreSQL, your migrations will contain DSL syntax like `t.datetime` and `t.timestamps` which will produce columns using the `timestamp` (`without time zone`) Postgres data type. While reading [A Simple Explanation of Postgres' Timestamp with Time Zone](https://naildrivin5.com/blog/2024/10/10/a-simple-explanation-of-postgres-timestamp-with-time-zone.html), I learned that there is a way to configure your app to instead use `timestamptz` by default. This data type is widely recommended as a good default, so it is nice that we can configure Rails to use it. First, add these lines to a new initializer (`config/initializers/postgres.rb`) file. ```ruby require "active_record/connection_adapters/postgresql_adapter" ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.datetime_type = :timestamptz ``` Alternatively, you can configure this via `config/application.rb` per the [Configuring ActiveRecord docs](https://guides.rubyonrails.org/configuring.html#activerecord-connectionadapters-postgresqladapter-datetime-type). Then, if you have a new migration like the following: ```ruby class AddEventsTable < ActiveRecord::Migration[7.2] def change create_table :events do |t| t.string :title t.text :description t.datetime :start_time t.datetime :end_time t.timestamps end end end ``` you can expect to have four `timestamptz` columns, namely `start_time`, `end_time`, `created_at`, and `updated_at`. Here is the [Rails PR](https://github.com/rails/rails/pull/41084) that adds this config option. ================================================ FILE: rails/set-default-as-sql-function-in-migration.md ================================================ # Set Default As SQL Function In Migration With static default values, like `0`, `true`, or `'pending'`, we can set them directly as the value of `default`. ```ruby class CreateActionsTable < ActiveRecord::Migration[7.2] def change create_table :actions do |t| t.string :status, default: 'pending' end end end ``` However, if we want our default value to be a SQL function like `now()`, we have to use a lambda. Let's extend the above example to see what that looks like: ```ruby class CreateActionsTable < ActiveRecord::Migration[7.2] def change create_table :actions do |t| t.string :status, default: 'pending' t.column :created_at, :timestamptz, default: -> { 'now()' }, null: false end end end ``` If we need to alter the default of an existing table's column, we can do something like this: ```ruby class AddDefaultTimestampsToActions < ActiveRecord::Migration[7.2] def up change_column_default :actions, :created_at, -> { "now()" } change_column_default :actions, :updated_at, -> { "now()" } end def down change_column_default :actions, :created_at, nil change_column_default :actions, :updated_at, nil end end ``` I believe this functionality is available to Rails 5.0 and later. [source](https://github.com/rails/rails/issues/27077#issuecomment-261155826) ================================================ FILE: rails/set-default-url-options-for-entire-application.md ================================================ # Set default_url_options For Entire Application There are a [number of ways](https://github.com/rails/rails/issues/29992#issuecomment-575464112) to set `default_url_options` for the various modules that need them. While the development server can infer URL options for the most part, it needs them explicitly defined for `ActionMailer`. You may also see the `ArgumentError: Missing host to link to! Please provide the :host parameter, set default_url_options[:host], or set :only_path to true` error when using a URL helper. That's because the `routes` config also needs to know. Instead of cobbling together default settings in several different places as the issues arise, you can define them application-wide like so: ```ruby # config/environments/development.rb Rails.application.default_url_options = { host: 'localhost', port: 3000 } Rails.application.configure do # ... end ``` Add the `default_url_options` to `Rails.application` just outside of the standard `configure` block in each of your environment config files. Now everything should know how to generate full URLs. Note: I've tested this out in a Rails 6.1 application. [source](https://discuss.rubyonrails.org/t/define-host-so-absolute-urls-work-in-development-and-test/75085/10) ================================================ FILE: rails/set-meta-tags-in-erb-views.md ================================================ # Set Meta Tags In ERB Views There are all kinds of meta tags that we may want to set for the pages that our Rails app serves. A lot of these are for SEO and social sharing. Let's look at how to add `og:description` meta tags to our views. I'll start with a helper method in `app/helpers/application_helper.rb`: ```ruby module ApplicationHelper def meta_description(desc) content_for(:description) { desc } end end ``` Then, I'll update my `app/views/layouts/application.html.erb` to consume the description when provided. ```ruby ``` Now I have a default description for all my views that I can override as needed with the `meta_description` helper. ```ruby # app/views/posts/show.html.erb <%= meta_description @post.body.split("\n").first %> ``` If I reload the page and inspect the meta tags in ``, I should find the `og:description` tag with the corresponding value. This can be extended to apply all the different meta tags (e.g. Open Graph and Twitter) to make links to these pages render well across the internet. ================================================ FILE: rails/set-schema-search-path.md ================================================ # Set Schema Search Path By default the schema search path for a PostgreSQL database is going to be `"$user", public`. Tables created by a Rails migration are going to end up on the `public` schema by default. If your application has other schemas in play, then you may want to ensure that those schemas are also on the schema search path. This can be accomplished by adding the `schema_search_path` setting to your `database.yml` file. For instance, to include both the `legacy` and `public` schema in the Postgres search path, add the following line: ```ruby schema_search_path: "legacy,public" ``` h/t Jack Christensen ================================================ FILE: rails/set-statement-timeout-for-all-postgres-connections.md ================================================ # Set Statement Timeout For All Postgres Connections The [`statement_timeout`](postgres/set-a-statement-timeout-threshold-for-a-session.md) setting in PostgreSQL allows you to head off long running queries and migrations that could break your deploys and lock up your production tables. This value can be set to a sensible default across all the connections your Rails app makes to PostgreSQL. To set it, open up your `config/database.yml` file and add a `variables` element to the default section. ```yaml default: &default adapter: postgresql encoding: unicode # For details on connection pooling, see Rails configuration guide # https://guides.rubyonrails.org/configuring.html#database-pooling pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %> variables: statement_timeout: 60000 ``` That's 60 seconds in milliseconds. You can avoid the mental math by using a string argument with a unit such as `s` for seconds. ```yaml variables: statement_timeout: '60s' ``` If you then execute a long running query, such as: ```ruby ActiveRecord::Base.connection.execute('select pg_sleep(62)') ``` It will terminate 2 seconds early because of the statement timeout. [source](https://til.hashrocket.com/posts/b44baf657d-railspg-statement-timeout-) ================================================ FILE: rails/set-the-default-development-port.md ================================================ # Set The Default Development Port For Rails 5+, Puma has been the default web server that gets installed with new Rails apps. Puma comes with some configuration in the `config/puma.rb` file. If you open that file up, you'll see a number of settings that can be configured. One of them is the `port` that `puma` uses. ```ruby # Specifies the `port` that Puma will listen on to receive requests; default is 3000. # port ENV.fetch('PORT') { 3000 } ``` It looks for a `PORT` value in the envionrment and uses that if it is present. That means you could run: ```bash PORT=5005 rails s ``` and Puma would server the local development server at `localhost:5005`. If that value is not present, it will fallback to `#fetch`'s block which contains `3000`. If you always want to local Rails development server to run at a port other than `3000`, all you need to do is update that line. ```ruby port ENV.fetch('PORT') { 5005 } ``` Now, running `rails s` on its own will start the dev server up at `localhost:5005`. [source](https://schneems.com/2017/03/13/puma-ports-and-polish/) ================================================ FILE: rails/show-pending-migrations.md ================================================ # Show Pending Migrations Rails comes with a built-in rake task that allows you to check the status of migrations in the project. ```bash $ rake db:migrate:status database: pokemon_development Status Migration ID Migration Name -------------------------------------------------- up 20150219143706 Create pokemon table down 20150228003340 Create stats table ``` For large projects with lots of migrations, this is going to be a lot of output, so you can trim it down with a simple `grep`: ```bash $ rake db:migrate:status | grep '^ down' down 20150228003340 Create stats table ``` [source](https://github.com/rails/rails/blob/master/activerecord/lib/active_record/railties/databases.rake#L91) ================================================ FILE: rails/show-rails-models-with-pry.md ================================================ # Show Rails Models With Pry With the [`pry-rails`](https://github.com/rweng/pry-rails) gem, you get some extra goodies in the Rails console for your project. One of those goodies is `show-models`, a command for printing out a list of all models in the rails project. Add and bundle the `pry-rails` gem, run `rails c`, and then run `show-models` to give it a go. ``` > show-models Pokemon id: integer name: string level: integer pokemon_type: varchar belongs_to Trainer created_at: datetime updated_at: datetime Trainer id: integer name: string has_many Pokemons ``` ================================================ FILE: rails/show-rails-routes-with-pry.md ================================================ # Show Rails Routes With Pry In [Show Rails Models With Pry](show-rails-models-with-pry.md), I showed that [`pry-rails`](https://github.com/rweng/pry-rails) comes with some handy console commands. In addition to being able to list all your Rails models, you can list all the routes for your application using `show-routes`. I get the following output by using that command in a small blog project: ``` > show-routes Prefix Verb URI Pattern Controller#Action root GET / application#index markdownify_articles POST /articles/markdownify(.:format) articles#markdownify articles POST /articles(.:format) articles#create new_article GET /articles/new(.:format) articles#new edit_article GET /articles/:id/edit(.:format) articles#edit article GET /articles/:id(.:format) articles#show PATCH /articles/:id(.:format) articles#update PUT /articles/:id(.:format) articles#update users POST /users(.:format) users#create new_user GET /users/new(.:format) users#new user GET /users/:id(.:format) users#show sessions POST /sessions(.:format) sessions#create new_session GET /sessions/new(.:format) sessions#new session DELETE /sessions/:id(.:format) sessions#destroy signin GET /signin(.:format) sessions#new POST /signin(.:format) sessions#create signup GET /signup(.:format) users#new ``` ================================================ FILE: rails/skip-validations-when-creating-a-record.md ================================================ # Skip Validations When Creating A Record Validations on your [ActiveRecord](https://api.rubyonrails.org/classes/ActiveRecord/Base.html) models are there for a reason. They provide application-level feedback about data that doesn't meet business requirements. In many cases those validations should also be pushed down to the database-layer in the form of constraints. Sometimes, though rarely and probably only in a testing or development context, you'll want to skip validations. This is how you can do that when creating a new record: ```ruby user = User.new( name: 'Josh', email: '', password: SecureRandom.uuid ) user.valid? #=> false user.errors.messages #=> {:email=>["can't be blank"]} user.save(validate: false) ``` After newing-up an object with invalid data, you can [save it with the `validate` option set to `false`](https://api.rubyonrails.org/classes/ActiveRecord/Persistence.html#method-i-save). This will skip ActiveRecord validations. Note: If you also have a database-layer constraint, this won't work. Perhaps for your use case you can get by with a new non-persisted record. ================================================ FILE: rails/specify-new-attributes-for-find-or-create-by.md ================================================ # Specify New Attributes For #find_or_create_by The ActiveRecord [`#find_or_create_by`](https://apidock.com/rails/v4.0.2/ActiveRecord/Relation/find_or_create_by) method is a handy way to get an object that represents a record. It will attempt to look up that record, usually based on a unique value or set of values. If it can find one, then that's the record you get. If nothing is found, then it will create a new record. New records tend to need more data than just the unique lookup attribute. There are a couple ways these other attributes can be specified. The first is by giving `#find_or_create_by` a block. ```ruby User.find_or_create_by(email: "some@email.com") do |new_user| new_user.admin = false new_user.password = params[:password] new_user.password_confirm = params[:password_confirm] end ``` Another approach is to precede the `#find_or_create_by` call with a [`#create_with`](https://apidock.com/rails/ActiveRecord/QueryMethods/create_with) call. ```ruby User.create_with( admin: false, password: params[:password], password_confirm: params[:password_confirm] ).find_or_create_by(email: "some@email.com") ``` In both cases, the extra attributes will not be applied to the `User` record in the case of a _find_; they are only used in the case of a _create_. ================================================ FILE: rails/temporarily-disable-strong-params.md ================================================ # Temporarily Disable strong_params I was recently doing a Rails upgrade. This old version of the app wasn't prepared to deal with `strong_params` right away. I already had a pile of migration path TODOs and fixes on top of addressing the `strong_params` issue. I decided to put off dealing with `strong_params` by adding this line the main controller. ```ruby # app/controllers/application_controller.rb def params request.parameters end ``` This bypasses the `strong_params` check for all controllers inheriting from `ApplicationController`. Remember, this is a _temporary_ fix. You'll eventually want to adhere to `strong_params` for your mass assignments. [source](https://stackoverflow.com/a/41163978/535590) ================================================ FILE: rails/temporarily-turn-off-pending-migrations-error.md ================================================ # Temporarily Turn Off Pending Migrations Error Whenever I'm working on an end-to-end feature in a Rails app, soon or later I am going to see the _Pending Migrations_ error page. I try to visit one of the routes in the browser and the Rails app serves this error page instead of my actual request response. This is typically what I want. If there are migrations just sitting there that haven't been run yet, that's probably an issue. Maybe I just pulled down the latest changes from my teammates. The app isn't going to work properly without whatever schema changes are prescribed in those pending migrations. The thing to do is run those migrations. In some special cases though, I know what I'm doing and I would like to operate my app locally with specific migrations not yet applied. To skip the error, I can change this `config/environments/development.rb` setting from: ```ruby config.active_record.migration_error = :page_load ``` to: ```ruby config.active_record.migration_error = false ``` I just need to make sure to switch it back when I'm done. [source](https://til.hashrocket.com/posts/ujcixh5rwi-rails-ignore-pending-migrations) ================================================ FILE: rails/test-for-a-subset-of-attributes-on-a-model.md ================================================ # Test For A Subset Of Attributes On A Model Let's say you are using RSpec to test some code that creates or updates an ActiveRecord object. To evaluate that the code is working, you want to check the value of a couple different attributes. Though you could have a series of assert statements to check each value, you may want to combine them into a single statement. ```ruby book = process_that_updates_book(params) expect(book.attributes).to match( "title" => 'Updated title', "publication_year" => 2001, # ... you have to check *every* attribute ) ``` The `#match` matcher requires that every attribute in the hash matches. We don't really care to check the `created_at`, `updated_at`, and other irrelevant attributes. Instead, we can `match` on [`hash_including`](https://rubydoc.info/gems/rspec-mocks/RSpec%2FMocks%2FArgumentMatchers:hash_including). Additionally, we can `symbolize_keys` on the `attributes` to make the expected data a little cleaner. ```ruby book = process_that_updates_book(params) expect(book.attributes).to match(hash_including( title: 'Updated title', publication_year: 2001, )) ``` If the specified attributes match, then the expectation will pass. If at least one of them doesn't, then it will fail. All other attributes are ignored. ================================================ FILE: rails/test-if-an-instance-variable-was-assigned.md ================================================ # Test If An Instance Variable Was Assigned When testing Rails controller actions, you'll often be making assertions about the response to your test request. You may also want to assert about the instance variables being set, as those are headed for your view layer. For instance, if an instance variable `@metadata` is supposed to be set in a `#show` controller action, you can assert that it is with [RSpec's `assigns`](https://relishapp.com/rspec/rspec-rails/docs/controller-specs): ```ruby describe "when given valid params" do it "sets the metadata" do get :show, params: valid_params expect(assigns(:metadata)).to match( identifier: "abc123", session_id: "fe98f08c-bf2f-4749-9f81-071d9cc7720e", ) end end ``` [source](https://stackoverflow.com/questions/2051373/how-to-test-instance-variable-was-instantiated-in-controller-with-rspec) ================================================ FILE: rails/test-if-deliver-later-is-called-for-a-mailer.md ================================================ # Test If deliver_later Is Called For A Mailer There are many ways to test in your controller whether emails are going out. A concise and quick way to check is just to see if a `deliver_later` happened. Depending on how your test environment is configured, this could look one of two ways. If you have your `queue_adapter` set to [`:inline`](https://api.rubyonrails.org/classes/ActiveJob/QueueAdapters/InlineAdapter.html), then a `deliver_later` will happen synchronously. So, the email will immediately end up in the `deliveries` box. ```ruby expect { post :password_reset, params: valid_params }.to change { ActionMailer::Base.deliveries.count }.by(1) ``` The behavior is a bit different if your `queue_adapter` is set to something like [`:test`](https://api.rubyonrails.org/classes/ActiveJob/QueueAdapters/TestAdapter.html). In this case, the email is going to be queued in the app's job queue. Since it is not immediately being sent, the expectation will have to be about the job queue instead. ```ruby expect { post :password_reset, params: valid_params }.to have_enqueued_job(ActionMailer::DeliveryJob) ``` We can even dig into more specifics like this: ```ruby expect { post :password_reset, params: valid_params }.to have_enqueued_job(ActionMailer::DeliveryJob) .with('UserMailer', 'password_reset', 'deliver_later', Integer) ``` ================================================ FILE: rails/test-out-url-and-path-helpers-in-the-console.md ================================================ # Test Out URL And Path Helpers In The Console Rails has fancy metaprogrammed URL and path helpers generated from the `config/routes.rb` file. There is a ton of configurability to these routes. It can sometimes be hard to know exactly how they'll behave or what the generated route helper will look like. In those cases, we may want to test them out in the console. The Rails console doesn't have the same things autoloaded as mailers and views where we tend use these route helpers. So, we can reference them through `Rails.application.routes.url_helpers`. From there we can run both `*_path` route helpers. ```ruby > Rails.application.routes.url_helpers .api_v1_post_with_slugged_title_path( slug: 123, slugged_title: 'a-recent-path' ) => "/api/v1/posts/123/a-recent-path" ``` and `*_url` path helpers. ```ruby > Rails.application.routes.url_helpers .api_v1_post_with_slugged_title_url( slug: 123, slugged_title: 'a-recent-path' ) => "http://localhost:3000/api/v1/posts/123/a-recent-path" ``` For the `*_url` path helpers, make sure you have [`default_url_options` set](set-default-url-options-for-entire-application.md). [source](https://stackoverflow.com/a/13553422/535590) ================================================ FILE: rails/truncate-almost-all-tables.md ================================================ # Truncate Almost All Tables The [`database_cleaner`](https://github.com/DatabaseCleaner/database_cleaner) gem is a handy way to make sure you have a consistent database context for each test example or suite. One `database_cleaner` strategy that can be used is the `truncation` strategy. This truncates the data from all the tables by default. This is not ideal for *fixed* tables that contain domain-specific data because you end up having to do way more test setup than should be necessary. Fortunately, specific tables can be excepted by the truncation strategy using the `except` option. For instance, if we have a standard set of roles for users of our application, we can except that table from truncation with a line like the following in our `rails_helper.rb` file: ```ruby DatabaseCleaner.strategy = :truncation, {:except => %w[roles]} ``` ================================================ FILE: rails/update-column-versus-update-attribute.md ================================================ # Update Column Versus Update Attribute Rails offers a whole variety of methods for making updates to the ActiveRecord objects in your app. Two unique, infrequently-used ones are [`#update_column`](https://devdocs.io/rails~5.2/activerecord/persistence#method-i-update_column) and [`#update_attribute`](https://devdocs.io/rails~5.2/activerecord/persistence#method-i-update_attribute). What is unique about them is that they are both ways of updating a record while skipping the validations defined on the model. So, how do they differ? A call to `#update_attribute` is still going to trigger any callbacks defined on the model and it will touch the `update_at` column. On the other hand, `#update_column` can be thought of as a way of directly interacting with the database -- callbacks are skipped and you are truly only touching the specified column, `updated_at` is left as is. The docs have this recommendation for `#update_attribute`: > This is especially useful for boolean flags on existing records. And for `#update_column`, they say this: > This is the fastest way to update attributes because it goes straight to the > database, but take into account that in consequence the regular update > procedures are totally bypassed. These are both useful in specific situations, but be sure to know their differences and to use them with caution. ================================================ FILE: rails/upgrading-your-manifest-for-sprockets-4.md ================================================ # Upgrading Your Manifest For Sprocket's 4 If you're upgrading [Rails](https://github.com/rails/rails) and it involves bumping the [Sprockets](https://github.com/rails/sprockets) dependency from 3.x to 4.x, you may need to update your `manifest.js`. > Since the default logic for determining top-level targets changed, you might > find some files that were currently compiled by sprockets for delivery to > browser no longer are. You will have to edit the `manifest.js` to specify > those files. > The `manifest.js` file is meant to specify what files to use as a top-level > target using sprockets methods `link`, `link_directory`, and `link_tree`. You can specify what top-level assets like so: ```javascript # app/assets/config/manifest.js //= link_tree ../images //= link_directory ../javascripts .js //= link_directory ../stylesheets .css //= link some_file.xml //= link some/nested/style.css ``` Read more about the [upgrade process here](https://github.com/rails/sprockets/blob/master/UPGRADING.md#manifestjs) as well as in [this blog post](https://eileencodes.com/posts/the-sprockets-4-manifest/). ================================================ FILE: rails/use-irb-and-ruby-flags-with-rails-console.md ================================================ # Use IRB And Ruby Flags With Rails Console The `rails console` command only accepts two flags `--environment` and `--[no-]sandbox`. However, the `rails console` is built on top of IRB and Ruby, so it can accept flags that those commands understand. You can't pass an IRB flag directly to `rails console` though because it doesn't consider those part of its repertoire. ```bash $ rails console --sandbox -r './vendor/setup_records.rb' ``` It will put the session in sandbox mode, but it doesn't know what to do with `-r`. We have to explicitly separate its flags from flags that should be passed through. This is done with the `--` CLI convention. ```bash $ rails console --sandbox -- -r './vendor/setup_records.rb' ``` Everything after the `--` gets passed along to IRB. ================================================ FILE: rails/use-ruby-extension-for-template-file.md ================================================ # Use .ruby Extension For Template File An interesting feature of Rails that I can't seem to find documented anywhere is that you can write a template file with plain Ruby by using the `.ruby` extension. For instance, you might want to render some JSON from a template. Instead of using `jbuilder` or `erb`, you can have a `show.json.ruby` file. This is also popular with Turbo Stream files -- e.g. `update.turbo_stream.ruby`. How this works is that the entire file is evaluated as if it were a `.rb` file. Then the return value of the final statement is what is returned and rendered by Rails. ```ruby author_byline = @book.authors.map(&:name).to_sentence data = { id: @book.id, title: @book.title, author: author_byline, status: @book.published_at > Time.current ? 'Coming Soon' : 'Published', publication_year: @book.published_at.year } data.to_json ``` That final line converts the hash of data that we've built up into a JSON string that can then be rendered by the controller action that corresponds to this view template. Similarly, you can have a Turbo Stream template `show.turbo_stream.ruby` that looks something like this: ```ruby [ turbo_stream.prepend("posts", @post), turbo_stream.update("form", partial: "form", locals: { post: Post.new }) ].join ``` This template file is made up of a single statement which is an array of turbo stream results that get joined together. ================================================ FILE: rails/useful-active-support-constants-for-durations.md ================================================ # Useful ActiveSupport Constants For Durations Whenever I'm passing a duration to a function, I like to [name it with the unit](https://ruudvanasseldonk.com/2022/03/20/please-put-units-in-names) relative to the value it represents. For instance, if I need to pass in an hour duration in seconds, I might write the following line: ```ruby hour_in_seconds = 60 * 60 # or hour_in_seconds = 3600 ``` ActiveSupport has a [Duration class](https://api.rubyonrails.org/classes/ActiveSupport/Duration.html) with a series of constants that we can reach for. ```ruby > ActiveSupport::Duration::SECONDS_PER_MINUTE => 60 > ActiveSupport::Duration::SECONDS_PER_HOUR => 3600 > ActiveSupport::Duration::SECONDS_PER_DAY => 86400 > ActiveSupport::Duration::SECONDS_PER_WEEK => 604800 > ActiveSupport::Duration::SECONDS_PER_MONTH => 2629746 > ActiveSupport::Duration::SECONDS_PER_YEAR => 31556952 ``` Though it is fun to know about these, we should keep in mind that this class provides support for what is likely to be a more useful abstraction layer: ```ruby > 1.hour => 1 hour > 3.hours => 3 hours > 1.day.to_i => 86400 ``` ================================================ FILE: rails/validate-column-data-with-check-constraints.md ================================================ # Validate Column Data With Check Constraints A check constraint is a feature of database systems like PostgreSQL that allows you to enforce rules about the data that goes in a table's column. As of Rails 6.1, ActiveRecord provides a way to add a check constraint via the DSL. In this example, we want to ensure that the value going into the reading_statuses.status column is one of four values. Nothing else besides these four values should be allowed. ```ruby class AddReadingStatusTable < ActiveRecord::Migration[7.2] def change create_table :reading_statuses do |t| t.references :user, null: false, foreign_key: true t.references :book, null: false, foreign_key: true t.string :status, null: false t.timestamps end add_check_constraint :reading_statuses, "status in ('started', 'completed', 'abandoned', 'already_read')", name: "reading_statuses_valid_status_check" end end ``` The `#add_check_constraint` method takes the name of the table and a SQL clause that can evaluate to true or false for a given row. We can optionally include the name of the check constraint (e.g. {table_name}_{column_name}_check) like we've done above. ================================================ FILE: rails/verify-and-read-a-signed-cookie-value.md ================================================ # Verify And Read A Signed Cookie Value Let's say a value was added as a [signed cookie](https://apidock.com/rails/ActionDispatch/Cookies/CookieJar/signed) in a request: ```ruby cookies.signed[:discount] = 45 #=> Set-Cookie: discount=BAhpMg==--2c1c6906c90a3bc4fd54a51ffb41dffa4bf6b5f7; path=/ ``` Generally to verify and read that value, you'd grab it from the signed cookies included in the request. ```ruby cookies.signed[:discount] #=> 45 ``` What if you have the signed cookie value, but not in the context of a `cookies` object? You can build a cookie jar from the current request and read the verified value from that. ```ruby cookie_value = 'BAhpMg==--2c1c6906c90a3bc4fd54a51ffb41dffa4bf6b5f7' cookie_hash = { discount: cookie_value } cookie_jar = ActionDispatch::Cookies::CookieJar.build(request, cookie_hash) cookie_jar.signed[:discount] #=> 45 ``` It is also possible to [Base64 decode the value](https://blog.bigbinary.com/2013/03/19/cookies-on-rails.html), however that doesn't ensure that the value hasn't been tampered with. [source](https://philna.sh/blog/2020/01/15/test-signed-cookies-in-rails/) ================================================ FILE: rails/where-am-i-in-the-partial-iteration.md ================================================ # Where Am I In The Partial Iteration? Let's say I am going to render a collection of posts with a post partial. ```erb <%= render collection: @posts, partial: "post" %> ``` The [`ActionView::PartialIteration`](http://api.rubyonrails.org/classes/ActionView/PartialIteration.html) module provides a couple handy methods when rendering collections. I'll have access in the partial template to `#{template_name}_iteration` (e.g. `post_iteration`) which will, in turn, give me access to `#index`, `#first?`, and `#last?`. This is great if I need to do something special with the first or last item in the collection or if I'd like to do some sort of numbering based on the index of each item. [source](http://stackoverflow.com/questions/13397848/rails-render-collection-partial-getting-size-of-collection-inside-partial) h/t Josh Davey ================================================ FILE: rails/why-redirect-and-return-in-controllers.md ================================================ # Why Redirect And Return In Controllers A fairly common idiom in Rails controller actions is a `redirect_to` followed by an `and return`. ```ruby def show redirect_to sign_in_path and return if current_user.blank? book = Book.find(params[:id]) render book end ``` Because a `render` comes later in the controller action, we need to _early return_ after the redirect to avoid multiple render/redirect warnings. It is important to use `and` here instead of `&&` because of logical operator precedence. If we used `&&` instead: ```ruby redirect_to sign_in_path && return if current_user.blank? ``` The `redirect_to` would get `nil` (the result of `sign_in_path && nil`). If we did want to use `&&`, we'd need to be diligent with method call parentheses. ```ruby redirect_to(sign_in_path) && return if current_user.blank? ``` In this case, `redirect_to` would actually get called with a path string and the `return` would be called after that. [source](https://stackoverflow.com/a/37211314/535590) ================================================ FILE: rails/wipe-out-all-precompiled-assets.md ================================================ # Wipe Out All Precompiled Assets You can clean up precompiled assets using: ```bash $ rails assets:clean ``` This command is built to be safe for situations like rolling deploys. Any assets that are still being used will not be cleaned up. If you really want to wipe out _all_ precompiled assets, you should _clobber_ them: ```bash $ rails assets:clobber ``` [source](https://edgeguides.rubyonrails.org/command_line.html#rails-assets) ================================================ FILE: rails/write-reversible-migration-to-set-default.md ================================================ # Write Reversible Migration To Set Default You can use the `change_column_default` method to alter the default value of a column. If the column doesn't have a default, then you'd essentially be changing the default from `nil` to _some value_. ```ruby def up change_column_default :books, :published, false end def down change_column_default :books, :published, nil end ``` This is fine, but you can write the migration as a single, reversible `change` method using the `:from` and `:to` options. ```ruby def change change_column_default :books, :published, from: nil, to: false end ``` When you migrate, the default will be set to `false`. When you rollback, the default will be removed. [source](https://blog.arkency.com/how-to-add-a-default-value-to-an-existing-column-in-a-rails-migration/) ================================================ FILE: rails/write-safer-where-clauses-with-placeholders.md ================================================ # Write Safer Where Clauses With Placeholders Ruby has a super ergonomic syntax for string interpolation. This can make it tempting to build up ActiveRecord `where` clauses like so: ```ruby def get_book_by_title(title) Book.where("lower(title) = #{title.downcase}") end ``` The `where` clause, as written, is vulnerable to a SQL injection attack. There are two kinds of placeholder syntax that you can use instead handle sanitization of the SQL. ```ruby def get_book_by_title(title) Book.where("lower(title) = ?", title.downcase) end ``` You can use multiple `?` in the query and they same number of following arguments will be interpolated in order. There is also the keyword placeholder syntax which can give you more flexibility and make the SQL read more clearly. ```ruby def get_book_by_title(title) Book.where("lower(title) = :title", title: title.downcase) end ``` [source](https://devdocs.io/rails~5.2/activerecord/querymethods#method-i-where) ================================================ FILE: react/a-component-is-just-a-bag-of-data.md ================================================ # A Component Is Just A Bag Of Data If you write enough React using JSX, it is easy to forget that you're not working with markup. Everything -- `div`s, `h1`s, 3rd party components, your components -- all get boiled down to JavaScript objects full of data. Any given React component is really just a bag of data. Try doing a `console.log` to see. Here is an example from an [earlier post](https://github.com/jbranchaud/til/blob/master/react/dynamically-add-props-to-a-child-component.md). ```javascript const ParentWithClick = ({ children }) => { return ( {React.Children.map(children || null, (child, i) => { console.log(child); return ; })} ); }; const App = () => (
    Click this span
    ); ``` Looking in the console, we see the following output: ``` Object {type: "span", key: null, ref: null, props: Object, _owner: Object…} type: "span" key: null ref: null props: Object children: "Click this span" _owner: Object _store: Object ``` It contains information about the component itself and because of the tree structure of this data, you could potentially expand the `props` --> `children` sections several times for certain components. See a [live example here](https://codesandbox.io/s/l41pj382x7). ================================================ FILE: react/access-the-latest-lifecycle-methods-in-an-old-app.md ================================================ # Access The Latest Lifecycle Methods In An Old App With [the release of React 16.3](https://reactjs.org/blog/2018/03/29/react-v-16-3.html) we have access to some [new lifecycle methods](https://reactjs.org/blog/2018/03/27/update-on-async-rendering.html) and are in the first phase of what will eventually result in the deprecation and removal of `componentWillMount`, `componentWillReceiveProps`, and `componentWillUpdate`. You may not be ready to move your project to React 16.3, but that doesn't mean you can't start migrating your lifecycle methods now. We'll need a polyfill -- [react-lifecycles-compat](https://github.com/reactjs/react-lifecycles-compat). ```javascript import React from 'react'; import { pollyfill } from 'react-lifecycles-compat'; class MyComponent extends React.Component { static getDerivedStateFromProps(nextProps, prevState) { // ... } render() { ... } } polyfill(MyComponent) export default MyComponent; ``` For any of our class components for which we'd like to start using the new lifecycle methods, we just need to import the polyfill function and then transform the class component with the polyfill before exporting it. [source](https://reactjs.org/blog/2018/03/27/update-on-async-rendering.html#open-source-project-maintainers) ================================================ FILE: react/accessing-env-vars-in-create-react-app.md ================================================ # Access Env Vars In create-react-app Environment-specific configurations are an important part of most applications. You can access environment variables in your create-react-app code using `process.env`. There are a couple built-in environment variables, such as `NODE_ENV`. Anything custom that you want to provide must be prepended with `REACT_APP_`. If it isn't, that environment variable will be ignored with no warning. The following line of code ```javascript const base_api_url = process.env.REACT_APP_BASE_API_URL; ``` will have access to whatever that value is in the environment when the server is started or the app is built. Set that value inline like so: ``` REACT_APP_BASE_API_URL="https://api.my_app.com" yarn start ``` [source](https://github.com/facebookincubator/create-react-app/blob/master/packages/react-scripts/template/README.md#adding-custom-environment-variables) ================================================ FILE: react/accessing-location-within-reach-router.md ================================================ # Accessing Location Within @reach/router The API of [`@reach/router`](https://reach.tech/router) departs a bit from [`react-router`](https://reacttraining.com/react-router/) in a couple ways. The `location` prop which you may be used to having access to automatically is instead available through the [`Location`](https://reach.tech/router/api/Location) component. ```javascript import React from 'react'; import { Location } from '@reach/router'; const MyComponent = () => { return ( {({ location }) => { return

    Current Location: {location.pathname}

    ; }}
    ); } ``` This is a contrived example, but you can imagine how you'd use it to access `state` or even create an HOC similar to `withRouter`. ================================================ FILE: react/allow-md-as-an-extension-with-gatsby-mdx.md ================================================ # Allow md As An Extension With gatsby-mdx The [gatsby-mdx](https://github.com/ChristopherBiscardi/gatsby-mdx) plugin allows you to create pages in a Gatsby project using `.mdx` files. If you prefer the `.md` extension on your markdown files, then you can adjust the plugin options to allow that. ```javascript // gatsby-config.js plugins: [ { resolve: `gatsby-mdx`, options: { extensions: [".mdx", ".md"] } } ] ``` This tells `gatsby-mdx` to recognize both `.mdx` and `.md` extensions when processing files. ================================================ FILE: react/alter-the-display-name-of-a-component.md ================================================ # Alter The Display Name Of A Component Components adopt their display name from the class or function that renders them. A component's display name becomes important to know about as soon as you start digging through the [React devtools](https://github.com/facebook/react-devtools) interface -- whether debugging or just perusing the component hierarchy, the display names of components is what you'll see. In most circumstances, the display name is good enough as is. If you want or need to, you can change it. ```javascript const Hello = ({ name }) => { return (

    {name}

    ); }; Hello.displayName = "Hola"; ``` By setting the `displayName` property on this component, you are able to alter what name is used by React devtools. This can be useful when bringing in a 3rd party library or component that doesn't use a display name that you find helpful -- in particular when using Higher Order Components. ================================================ FILE: react/building-a-react-app-in-the-browser.md ================================================ # Building A React App In The Browser There are a couple relatively new tools that give you just about everything you need to build a React app in the browser. The first is [CodeSandbox](https://codesandbox.io/). It has a couple pre-configured environments for different technologies. The main one is React. CodeSandbox describes itself as such: > an online editor that helps you create web applications, from prototype to > deployment. The second, which just released out of beta, is [Glitch](https://glitch.com/) which comes with projects like [react-starter](https://glitch.com/~starter-react) so that you can jump into a React project that is ready to roll. Glitch describes itself as: > the friendly community where you'll build the app of your dreams You can quickly build and publish React apps in either of these and share them with the world. ================================================ FILE: react/check-the-type-of-a-child-component.md ================================================ # Check The Type Of A Child Component There is a simple way to check the type of a child component. ```javascript import React from 'react'; const Hello = () =>

    Hello

    ; const Greeting = ({ children }) => { let hello; React.Children.forEach(children, child => { if(child.type === Hello) { hello = child; } }); return hello; }; ``` This is a comparison of the child's type to the component constant we are looking for. This comparison is not the most robust. For instance, Gatsby does something internally that throws off this comparison. Here is a more robust comparison. ```javascript if(child.type === Hello || child.type === .type) ``` [source](https://github.com/gatsbyjs/gatsby/issues/3486) ================================================ FILE: react/conditionally-including-event-handler-functions.md ================================================ # Conditionally Including Event Handler Functions React makes a variety of [synthetic DOM events](https://reactjs.org/docs/events.html) available to your component. Events such as `onClick`, `onKeyPress`, `onSubmit`, etc. When specifying one of these event handlers, you must supply a function. To conditionally include an event handler, you may be tempted to do this: ```javascript ``` This means that `onKeyPress` will receive `false` when `someCondition` is `false`. That is a prop type violation. Instead, you should use a ternary statement. ```javascript ``` If `someCondition` is `false`, then the prop will be set as `undefined` and that prop won't be defined. ================================================ FILE: react/create-a-snowpack-bundled-react-app.md ================================================ # Create A Snowpack-Bundled React App > [Snowpack](https://www.snowpack.dev/) is a modern, lightweight toolchain for > web application development. You can get started using snowpack with this `npx` command. ```bash npx create-snowpack-app my-app ``` This command uses a base template that is pre-configured to use the snowpack defaults. You can take this a step further by using a specialized template for React apps. ```bash npx create-snowpack-app react-client --template @snowpack/app-template-react ``` An advantage this has over [create-react-app](https://github.com/facebook/create-react-app) is there is no ejecting needed to do something like pull in [PostCSS](https://postcss.org/). ================================================ FILE: react/create-dynamically-named-custom-react-components.md ================================================ # Create Dynamically Named Custom React Components A React element is as simple as a function that returns some valid JSX. Any function will do. ```javascript const CustomComponent = ({ children }) => { return ( {children} ); }; ``` This function provides us with a React component that has a fixed name -- `CustomComponent`. With the help of the [`displayName` property](https://reactjs.org/docs/react-component.html#displayname), we can create dynamically named components. ```javascript const ComponentGenerator = ({ customName, children }) => { const CustomComponent = ({ children }) => { return ( {children} ); }; CustomComponent.displayName = customName; return ( {children} ); }; const App = () => { return ( Hello! ); } ``` If we inspect the generated React tree, we will not see anything called ``, but instead we will see our `` component. Remember, React components need to have an uppercase name. ================================================ FILE: react/create-react-app-comes-with-lodash.md ================================================ # create-react-app Comes With Lodash [Lodash](https://lodash.com/) is already a dependency of [create-react-app](https://github.com/facebook/create-react-app). If you need one of those fancy Lodash functions, there's no need to `yarn add lodash` to your project, all you need is an import statement. ```javascript import chunk from 'lodash/chunk'; ``` h/t Dillon Hafer ================================================ FILE: react/create-react-app-has-a-default-test-setup-file.md ================================================ # create-react-app Has A Default Test Setup File In [_Configure Jest To Run A Test Setup File_](https://github.com/jbranchaud/til/blob/master/javascript/configure-jest-to-run-a-test-setup-file.md), I pointed to a way of configuring Jest in either the `package.json` or `jest.config.js` file with the `setupTestFrameworkScriptFile` value. In a `create-react-app` project, this is not an option because `setupTestFrameworkScriptFile` is not one of the permitted config values for Jest. There is a built-in value which happens to match what was recommended in the above post -- `src/setupTests.js`. This means that there is no configuration required. Instead, just create a `setupTests.js` file in the `src` directory of your CRA project and add any framework setup you need there. That file is already configured to run when you invoke `yarn test`. [source](https://github.com/facebook/create-react-app/blob/master/packages/react-scripts/template/README.md#initializing-test-environment) ================================================ FILE: react/css-important-is-not-supported-by-inline-styles.md ================================================ # CSS !important Is Not Supported By Inline Styles You can get pretty far with React's inline styling of components. There are however a few limitations. One such limitation is that the `!important` clause is not supported. If you try applying `!important` to an inline style like so: ```jsx
    My div
    ``` You'll be disappointed when you open up the browser and inspect that `div` tag. The `color` rule will be ignored and excluded from the output html. [source](https://github.com/facebook/react/issues/1881) ================================================ FILE: react/debug-jest-tests-in-create-react-app.md ================================================ # Debug Jest Tests In create-react-app When you put a `debugger;` statement in one of your Jest tests and run `yarn test`, the test runner will ignore the debug statement and run to completion. This is because Jest defaults to parallelizing tests which won't mix well with manual debugging intervention. If we want to be able to run our Jest tests through a debugger. We will need two things. First, we need a debugging environment -- Chrome's devtools will work well for this. Second, we need our tests to run in band. The [`react-scripts` documentation](https://github.com/facebook/create-react-app/blob/master/packages/react-scripts/template/README.md#debugging-tests) recommends adding a second, debug-specific test command to your `package.json`: ```javascript "scripts": { "test:debug": "react-scripts --inspect-brk test --runInBand --env=jsdom" } ``` You can now run `yarn test:debug` which will start a paused debug session. Open chrome at `chrome://inspect` to access and open the debugging session panel. Now, debug away. ================================================ FILE: react/defining-state-in-a-simple-class-component.md ================================================ # Defining State In A Simple Class Component Most class components start off with a constructor which does some initialization of the component including setting the components initial state. It might look something like the following: ```javascript class MyComponent extends React.Component { constructor(props) { super(props); this.state = { loading: true }; } render() { if (this.state.loading) { return (

    Loading...

    ); } else { // ... } } } ``` If setting state is the only thing you need to do in the constructor, then you can skip the constructor all together. ```javascript class MyComponent extends React.Component { state = { loading: true }; render() { if (this.state.loading) { return (

    Loading...

    ); } else { // ... } } } ``` This second example will work the same as the first, and it is a bit more concise. ================================================ FILE: react/destructure-variables-as-props-to-a-component.md ================================================ # Destructure Variables As Props To A Component When passing down props, a redundant-feeling pattern can sometimes emerge. ```javascript const MyComponent = ({ handleChange, handleBlur }) => { return (
    ) }; ``` The typing feel duplicative, as if there ought to be a better way. One option is to simply pass down all the props: ```javascript ``` This approach may result in passing down props that we don't intend to pass down and clutters the flow of data in our app. Here is another approach: ```javascript const MyComponent = ({ handleChange, handleBlur }) => { return (
    ) }; ``` Here we are taking advantage of two ES6 features. Since the naming is the same, we can use [property shorthands](http://es6-features.org/#PropertyShorthand). Then we immediately use the [spread operator](http://es6-features.org/#SpreadOperator) to splat it back out as the props to the component. h/t Vidal Ekechukwu ================================================ FILE: react/details-tags-are-a-controllable-component.md ================================================ # Details Tags Are A Controllable Component HTML has a lovely, but little-used tag for summarizing a set of collapsed details. These details can be expanded and recollapsed by clicking the summary. This is the `
    ` tag and it can be controlled with the `open` prop and `onToggle` handler. ```jsx import React, { useState } from "react"; export default function Details({ summary, children, onToggle }) { const [open, setOpen] = useState(false); return (
    { setOpen(prev => !prev); onToggle(); }} > {summary} {children}
    ); } ``` [live example](https://codesandbox.io/s/loving-merkle-hxlut?file=/src/App.js:0-545), [source](https://github.com/facebook/react/issues/15486) ================================================ FILE: react/dispatch-anywhere-with-redux.md ================================================ # Dispatch Anywhere With Redux Your React app is going to have a single top-level `store` which is connected to the app with the `Provider` component. Most of the time, when you create a connected component, you'll create prop functions that dispatch on a redux action. This isn't the only place you can dispatch though. If you export your `store`, then it can be imported anywhere along with its `dispatch` function. ```javascript // src/index.js export const store = createStore(rootReducer); ``` ```javascript // src/components/MyComponent.js import { store } from '../index'; import { updateData } from '../actions'; // ... componentDidMount() { getData().then((json) => { store.dispatch(updateData(json)); } } ``` See the [`dispatch`](https://redux.js.org/docs/api/Store.html#dispatch) documentation for more details. ================================================ FILE: react/dynamically-add-props-to-a-child-component.md ================================================ # Dynamically Add Props To A Child Component If your component has an element nested in it, then it will receive a `children` prop. There are a number of things you can do beyond simply including the `children` as part of the rendered output of the component. One thing you can do is put additional props on the child. ```javascript const ParentWithClick = ({ children }) => { return ( alert("You clicked me!")} /> ); }; ``` This `ParentWithClick` component will reconstitute its child component with its given props and a new `onClick` prop. Here is how it can be used: ```javascript const App = () => { return ( Hello! ); }; ``` Click on `Hello!` and you'll see the alert. Minor caveat: multiple children and a string child will need to be handled differently. See a [live example here](https://codesandbox.io/s/n0pyn61yop). ================================================ FILE: react/dynamically-create-html-elements.md ================================================ # Dynamically Create HTML Elements An HTML element can be created with a string that matches a recognized entity. ```javascript const Paragraph = 'p'; return Some paragraph content ``` This means we can dynamically create HTML elements such as headers: ```javascript const H = ({ level, ...props }) => { const Heading = `h${Math.min(level, 6)}`; return ; }; return ( Header 1 Header 2 Header 5 ); ``` With some [inspiration](https://medium.com/@Heydon/managing-heading-levels-in-design-systems-18be9a746fa3), I've created a [live example here](https://codesandbox.io/s/3v202wmmy1). ================================================ FILE: react/enforce-specific-values-with-proptypes.md ================================================ # Enforce Specific Values With PropTypes Being able to constrain our user interfaces to very specific values is valuable. This makes our interfaces easier to reason about and easier to test. PropTypes in general are one of the ways that we constrain our UIs. We can go even further than simple type constraints by limiting a prop to a specific set of values, an enum if you will. ```javascript MyComponent.propTypes = { flavor: PropTypes.oneOf(['Vanilla', 'Chocolate', 'Strawberry']), }; ``` The docs say about `oneOf()`: > You can ensure that your prop is limited to specific values by treating it > as an enum. If we use `MyComponent` with a value such as `Pistachio`, we'll have a console warning to answer for. [source](https://reactjs.org/docs/typechecking-with-proptypes.html) ================================================ FILE: react/focus-an-input-with-useref-hook.md ================================================ # Focus An Input With useRef Hook You can send focus to an input by calling [`focus()`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLOrForeignElement/focus) on it. To do this in a React context, you need to have a reference to the underlying DOM node. You can get this reference with the [`useRef` hook](https://reactjs.org/docs/hooks-reference.html#useref). ```jsx import React, { useRef } from 'react'; const MyComponent = () => { const inputRef = useRef(null); const focusInput = () => { inputRef.current.focus(); } return (
    ); } ``` When this component mounts, the underlying `` element will get tied to `inputRef`. The `focusInput` handler that I've created can be used to send focus to the corresponding `inputRef`. To demonstrate, I've passed it to the `onClick` handler of the button. Clicking the button will focus the input. ================================================ FILE: react/force-a-component-to-only-have-one-child.md ================================================ # Force A Component To Only Have One Child A component can normally have an arbitrary number of elements nested directly inside it. React's `Children.only` function can be used to force it to a single direct child. ```javascript import React, { Children, Component } from "react"; class App extends Component { render() { return ( There can only be one! ); } } const SingleChildContainer = props => { return Children.only(props.children); }; export default App; ``` The React docs describe the behavior of `Children.only` as such, _"Returns the only child in children. Throws otherwise."_. If you modify the `return` in `App` to contain the following JSX ```javascript There can only be one!
    What about me?!
    ``` then an error will be thrown (`React.Children.only expected to receive a single React element child`). The [`Provider` component](https://github.com/reactjs/react-redux/blob/master/src/components/Provider.js#L36) in [`react-redux`](https://github.com/reactjs/react-redux) is an example of where this is used. ================================================ FILE: react/forcing-a-child-remount-with-the-key-prop.md ================================================ # Forcing A Child Remount With The Key Prop There are a couple `prop` names that have reserved usage in React. One of those is `key`. We generally use `key` when we are rendering a list of things. It is a way of uniquely identifying each element in a list so that React minimizes re-rendering when parts of the list change. We can flip this on its head and utilize `key` as a way of forcing a remount and re-render of a child component. Imagine I have a component that does a number of things including rendering a component with some internal state, such as a counter. ```javascript class MyComponent extends React.Component { state = { remountKey: (new Date()).getTime(), } resetCounter = () => { this.setState({ remountKey: (new Date()).getTime(), }); } render() { return (
    {/* some other stuff in my component */}
    ); } } ``` I can force this `Counter` component to remount, thus resetting its state by passing it a new `key` value. The `button` can trigger this by updating the `remountKey` value. ================================================ FILE: react/formik-connected-components.md ================================================ # Formik Connected Components Formik comes with a `connect()` HOC that uses the context API as a way of connecting to disparate form elements. This means that form data, change handlers, touched and error data, etc. can be easily accessed without a lot of [prop drilling](https://kentcdodds.com/blog/prop-drilling). Any component that lives somewhere downstream in the tree of a Formik form can use `connect()`. ```javascript import { connect, getIn } from "formik"; const Input = ({ type = "text", name, id, label, formik }) => { return ( {" "} ); }; export default connect(Input); ``` This `Input` component is wrapped in `connect` which means it gets the `formik` prop which contains everything that we mentioned and more -- all the context you'll need to make your form element work. You can play around with it using this [live example](https://codesandbox.io/s/quizzical-hill-7xlwi). ================================================ FILE: react/formiks-validation-schema-as-a-function.md ================================================ # Formik's Validation Schema As A Function The most straightforward way to use [Formik](https://jaredpalmer.com/formik)'s `validationSchema` is to provide it with a [Yup](https://github.com/jquense/yup) object defining your form's validations. ```javascript const MyComponent = withFormik({ // ... validationSchema: yup.object().shape({ email: yup.string().required(), feedback: yup.string().required(), }), // ... })(MyForm); ``` There may be a point at which you need access to the `props` being passed to `MyComponent` in order to construct the proper set of validations. Formik supports this by allowing `validationSchema` to be a function. ```javascript const MyComponent = withFormik({ // ... validationSchema: (props) => { let emailSchema; if(props.allowAnonymous) { emailSchema = yup.string(); } else { emailSchema = yup.string().required(); } return yup.object().shape({ email: emailSchema, feedback: yup.string().required(), }); }, // ... })(MyForm); ``` When `validationSchema` is a function, its first argument is the set of props passed to that component. ================================================ FILE: react/inactive-and-active-component-styles-with-radium.md ================================================ # Inactive And Active Component Styles With Radium [Radium](https://github.com/FormidableLabs/radium) is "toolchain for React component styling" allowing you to do comprehensive inline styling with CSS. Often times, especially in the case of a series of nav elements, there is a need to style one element as _active_ while styling the rest as _inactive_. This can be achieved with Radium by defining two groups of styles (`base` and `active`) and then relying on props to conditionally apply the active style. ```javascript import React from 'react'; import Radium from 'radium'; const styles = { base: { textDecoration: "none", color: "gray", }, active: { color: "black", backgroundColor: "lightgray", }, }; let NavItem = ({ label, path, active }) => { return ( {label} ); }; NavItem = Radium(NavItem); ``` With Radium, our `base` (_inactive_) styles always get applied. Then, the `active` styles only get applied when the `active` prop is true. We produce a Radium-ified version of our `NavItem` on the last line so that Radium can handle all of the styling of the component. ================================================ FILE: react/inline-style-attributes-should-be-camel-cased.md ================================================ # Inline Style Attributes Should Be Camel Cased When adding a few quick styles to React components, you can add it directly on the tags in the JSX. To do this, use the `style` tag with a plain old JavaScript object of styles. ```javascript
    ``` If you are using a CSS attribute that is normally hyphenated like `padding-top` or `background-color`, you'll need to camel case it in the JSX. ```javascript
    ``` This is because our styles now need to conform to JavaScript syntax rules since they are in the form of a POJO. Read the [documentation](https://reactjs.org/docs/dom-elements.html#style) for more details. ================================================ FILE: react/manage-state-in-a-functional-component.md ================================================ # Manage State In A Functional Component Before the introduction of React 16.8, you had a couple options for declaring and managing state in your components. The first _class_ way was to create a class component and then [add local, component state to it](https://reactjs.org/docs/state-and-lifecycle.html#adding-local-state-to-a-class). If you already had a functional component, you could avoid the conversion to a class component with custom HOCs and Render Prop components or any number of third-party libraries such as [React PowerPlug](http://rena.to/react-powerplug/#/docs-components-state) and [Recompose](https://github.com/acdlite/recompose). However, projects using React 16.8+ have [Hooks](https://reactjs.org/docs/hooks-intro.html) at their disposal. The Hooks API's base offering is a state hook -- [`useState`](https://reactjs.org/docs/hooks-state.html). ```javascript import React, { useState } from "react"; const Toggler = () => { const [on, setOn] = useState(false); const [toggleCount, setToggleCount] = useState(0); const incrementToggleCount = setToggleCount(prev => prev + 1); const handleToggle = () => { setOn(prev => !prev); incrementToggleCount(); }; return (

    Toggle Count: {toggleCount}

    ); } ``` You can manage a variety of state values in a functional component with `useState`. The `useState` function takes the initial state value as an argument and returns a tuple with the current state value and an _setter_ function for updating that piece of state. ================================================ FILE: react/mapping-over-one-or-many-children.md ================================================ # Mapping Over One Or Many Children In [Dynamically Add Props To A Child Component](https://github.com/jbranchaud/til/blob/master/react/dynamically-add-props-to-a-child-component.md), I talked about how a child element can be reconstituted with additional props. The approach I showed will only work in the case of a single child being nested in that component. What if you want your component to account for one, many, or even children? React comes with a built-in function for mapping that handles these cases. ```javascript const ParentWithClick = ({ children }) => { return ( {React.Children.map(children || null, (child, i) => { return ; })} ); }; ``` The [`React.Children.map` function](https://reactjs.org/docs/react-api.html#reactchildrenmap) allows mapping over one or many elements and if `children` is `null` or `undefined`, it will return `null` or `undefined` respectively. See a [live example here](https://codesandbox.io/s/kwj29y2j2r). ================================================ FILE: react/mock-a-function-that-a-component-imports.md ================================================ # Mock A Function That A Component Imports You have a component that relies on an imported function, `isAuthenticated()`. ```javascript // MyComponent.js import React from 'react'; import { isAuthenticated } from './authentication'; const MyComponent = (props) => { if (isAuthenticated()) { return (
    {/* ... */}
    ); } else { return (
    Not authenticated
    ); } }; ``` You'd like to test that component without having to manage the authentication of a user. One option is to mock out that function. This can be done with some help from [`jest.fn()` and the `mock.mockReturnValue()` function](https://github.com/jbranchaud/til/blob/master/javascript/mock-a-function-with-return-values-using-jest.md). ```javascript // MyComponent.test.js // ... various testing imports import * as authModules from './authentication'; it('renders the component', () => { authModules.isAuthenticated = jest.fn().mockReturnValue(true); const wrapper = shallow(); expect(toJson(wrapper)).toMatchSnapshot(); }); ``` By importing the same module and functions used by `MyComponent`, we are then able to replace them (specifically, `isAuthenticated`) with a mocked version of the function that returns whatever value we'd like. As `MyComponent` is being rendered, it will invoked our mocked version of `isAuthenticated` instead of the actual one. You could test the other direction like so: ```javascript authModules.isAuthenticated = jest.fn().mockReturnValue(false); ``` ================================================ FILE: react/navigate-with-state-via-reach-router.md ================================================ # Navigate With State Via @reach/router With [@reach/router](https://reach.tech/router), you can programmatically change your route using the [`navigate`](https://reach.tech/router/api/navigate) function. This utilizes the Context API, so its available anywhere nested under your router. To provide some data to the destination location, include a `state` option in the `navigate` call. ```javascript const onSubmit = ({ data }) => { /* submit logic ... */ navigate(nextPath, { state: { data }}); } ``` The component that renders in response to this navigation will have access to this state. ```javascript const NextComponent = ({ location }) => { const { data } = location.state; return ( /* ... */ ) } ``` ================================================ FILE: react/pairing-a-callback-with-a-usestate-hook.md ================================================ # Pairing A Callback With A useState Hook React's Class-based state management allowed you to update the state of your component with a call to `this.setState()`. The first argument represents the changes to the state. It also accepts a second argument; a callback that will be invoked after the state has been updated. ```javascript this.setState({ loading: true }, () => console.log("Loading...")); ``` If you've transitioned to Hooks-based state management, then you may have noticed that the updaters generated by `useState` calls do not accept a second callback argument. If you want to update state and fire a callback in response to it, you can pair `useState` with `useEffect`. ```javascript import React, { useState, useEffect } from "react"; function App() { const [loading, setLoading] = useState(false); const toggleLoading = () => setLoading(prevLoading => !prevLoading); useEffect(() => { if(loading) { console.log("We are loading now"); } }, [loading]) return (
    {loading &&

    Loading...

    }
    ); } ``` The `useState` acts on its own. It has no side-effects. We follow it with a `useEffect` that responds to changes to the value of `loading` -- this is where our _callback_ gets invoked. See a [live example](https://codesandbox.io/s/clever-roentgen-kvzze). ================================================ FILE: react/pass-a-function-to-a-usestate-updater.md ================================================ # Pass A Function To A useState Updater Let's say you have a component with a toggle state: ```javascript const [toggle, setToggle] = useState(false); ``` You can change the state of the toggle by directly passing a value to `setToggle`. ```javascript setToggle(true); console.log(toggle); //=> true ``` Alternatively, you can pass a function to the updater. This is called a [_functional update_](https://reactjs.org/docs/hooks-reference.html#functional-updates). The updater will call the function with the previous state value and update the state to whatever the function returns. ```javascript const handleToggle = (prevToggle) => { return !prevToggle; } console.log(toggle); //=> true setToggle(handleToggle); console.log(toggle); //=> false ``` ================================================ FILE: react/passing-props-down-to-react-router-route.md ================================================ # Passing Props Down To React-Router Route When using [react-router](https://github.com/ReactTraining/react-router), you'll often use the `component` prop to have a certain component rendered. ```javascript ``` If, however, you need to pass props down into `MyComponent`, then you'll want to use the `render` prop with an inline function. ```javascript ( )} /> ``` The two spread operator statements are essential. They will pass down the [route props](https://reacttraining.com/react-router/web/api/Route/Route-props) that `Route` would have passed down plus the additional set of props that you want to pass down. ================================================ FILE: react/prevent-reach-router-redirect-error-screen-in-dev.md ================================================ # Prevent reach/router Redirect Error Screen In Dev When using [@reach/router's ``](https://reach.tech/router/api/Redirect) with tools like create-react-app and Gatsby, you'll get those tools' development-mode error screen overlays whenever a redirect happens. This has to do with how @reach/router utilizes `componentDidCatch` to change the path without a render. That error screen overlay can get annoying though. Prevent it with the `noThrow` prop. ```javascript return ( ); ``` ================================================ FILE: react/proxy-to-an-api-server-in-development-with-cra.md ================================================ # Proxy To An API Server In Development With CRA [create-react-app](https://github.com/facebookincubator/create-react-app) is a great way to bootstrap a React project, especially if you are building a single-page app. When building an SPA, you more likely than not will have a backend API that you interact with. You can set up your React app to interact with that backend API server in development using the `proxy` configuration in `package.json`. ```json // package.json ... "proxy": "http://localhost:4000", } ``` This will allow you to keep your API calls nice and clean. ```javascript fetch("/api/session", ... ``` No need to manage some sort of _host URL_ environment variable. Additionally, this will remove an CORS issues because the `webpackDevServer` will be proxying any paths that it doesn't recognize to the host and port that you've specified. See [the `create-react-app` docs](https://github.com/facebookincubator/create-react-app/blob/master/packages/react-scripts/template/README.md#proxying-api-requests-in-development) for more details. ================================================ FILE: react/quickly-search-for-a-component-with-react-devtools.md ================================================ # Quickly Search For A Component With React DevTools As the size of your React app grows, it can become challenging to track down specific components within the [React DevTools](https://github.com/facebook/react-devtools) extension. You opened it up with the hopes of quickly inspecting the `props` being received by a component, but find yourself navigating through the DOM structure instead. The React DevTools extension provides a search bar that can be used to quickly filter out most components. ![react devtools component search](https://i.imgur.com/dYd8SZD.gif) The search bar is located at the top of the extension. Once you start typing the results are immediate. Then, if you hover over any of the remaining components, you'll get some visual feedback as they are highlighted in the browser. ================================================ FILE: react/reach-router-renders-to-a-div.md ================================================ # @reach/router Renders To A Div Check out the following snippet that uses [`@reach/router`](https://reach.tech/router). ```javascript import { Router } from '@reach/router'; const Home = () =>

    Home

    ; const App = () => { return (
    ); } ``` When you visit '/home', this will render in the DOM as: ```html

    Home

    ``` Notice the extra `div` -- that is what `` renders to as part of `@reach/router`'s accessibility features. This may throw off the structure or styling of your app. This can be fixed. Any props that you give to `` will be passed down to that `div`. For instance, you could remove the most outer `div` and put `className="main"` on the ``. [source](https://github.com/reach/router/issues/63#issuecomment-395988602) ================================================ FILE: react/read-only-input-elements.md ================================================ # Read Only Input Elements Here is an input element with a `value` and no `onChange` handler. ```javascript const MyInput = ({ value }) => { return ( ); }; ``` React will raise a warning regarding the `input` element because it has a `value` without an `onChange` handler leaving React to wonder if it is intended to be a _controlled_ or _uncontrolled_ component. If our intention is to have the `value` set but not allow the user to directly change it, we just need to let React know that. ```javascript const MyInput = ({ value }) => { return ( ); }; ``` The `readOnly` prop means we don't intend for the input to be modified by user input. The React warning will now go away. h/t Dillon Hafer ================================================ FILE: react/rendering-multiple-nodes-with-fragments.md ================================================ # Rendering Multiple Nodes With Fragments When rendering, React expects a component to only return a single node. The DOM hierarchy of most components will easily follow this rule, but what about those components that do have multiple inline nodes? The two solutions have been to either wrap the inline nodes in an outer `div` or to return them as a comma separated array of nodes. With React 16, we can avoid the deficiencies of both of these approaches by using a fragment. Just wrap your inline nodes with a `React.Fragment` node. React will understand your JSX without wrapping the output DOM in a superfluous `div`. ```javascript render() { return (

    Name: {firstName} {lastName}

    Email: {email}

    Age: {age}

    ); } ``` See the [docs on fragments](https://reactjs.org/docs/fragments.html) for more details. ================================================ FILE: react/set-the-type-for-a-usestate-hook.md ================================================ # Set The Type For A useState Hook TypeScript can often infer the type of a `useState` hook. For instance, in the following example, TypeScript infers a type of `boolean`: ```typescript const [open, setOpen] = React.useState(false); ``` If we have a `useState` hook that can be `null` or a string: ```typescript const [error, setError] = React.useState(null); setError('There was an error'); // Argument of type 'string' is not assignable // to parameter of type 'SetStateAction' ``` then we'll get a TypeScript warning when we violate the inferred type of `SetStateAction`. The `useState` can be appropriate typed for this situation like so: ```typescript const [error, setError] = React.useState(null); ``` [source](https://www.carlrippon.com/typed-usestate-with-typescript/) ================================================ FILE: react/specifying-dependencies-of-a-useeffect-hook.md ================================================ # Specifying Dependencies Of A useEffect Hook The `useEffect` hook is all about performing side-effects. For instance, you'll want to place API calls within `useEffect` hooks. The dependency array -- the second argument to `useEffect` -- is where you declare all of the values that are depended on within the `useEffect`. If you're making an API call, this array is likely made up of parameters passed to that call. Here is a contrived example of what that could look like: ```javascript const apiCall = (opts) => Promise.resolve(opts); const [param1, param2, param3] = [1,2,3]; useEffect(() => { const handleApiCall = async () => { apiCall({ param1, param2, param3 }) .then((data) => { // do something with the data }) .catch((error) => { // do something with the error }); } handleApiCall(); }, [param1, param2, param3]); ``` If you don't specify all of the values used in the body of the `useEffect`, you are opening yourself up to potentially incorrect code. It is safer to specify all of them. The [`exhaustive-deps` rule](https://www.npmjs.com/package/eslint-plugin-react-hooks) can help. [This section](https://overreacted.io/a-complete-guide-to-useeffect/#what-happens-when-dependencies-lie) of Dan Abramov's "A Complete Guide to useEffect" does an excellent job of showing how things can go wrong when you lie to React about your dependencies. ================================================ FILE: react/spelunking-through-components-with-enzymes-dive.md ================================================ # Spelunking Through Components With Enzyme's Dive Most of the components we write have other components nested in them. ```javascript const Hello = ({ name }) =>

    Hello {name}!

    ; const HelloContainer = (props) => (
    ); ``` If we are to [shallow render the above component using Enzyme](http://airbnb.io/enzyme/docs/api/ShallowWrapper/shallow.html), we'll only see things one layer deep: ```javascript const wrapper = shallow(); // wrapper ~=
    ``` If we'd like to explore a particular child of the rendered component further, we can do a little [`find`](http://airbnb.io/enzyme/docs/api/ReactWrapper/find.html) and [`dive`](http://airbnb.io/enzyme/docs/api/ShallowWrapper/dive.html). ```javascript const wrapper = shallow(); const helloWrapper = wrapper.find(Hello).dive(); expect(helloWrapper.text()).toEqual("Hello World!"); ``` This allows us to make pinpoint assertions about how our components render without mounting the entire thing. See a [live example here](https://codesandbox.io/s/y236wr1kn1). h/t Vidal Ekechukwu ================================================ FILE: react/sync-your-react-router-state-with-redux.md ================================================ # Sync Your react-router State With Redux If you are building a React app that uses both `redux` and `react-router`, you'll find that you are managing app state in two places. Most of your app state is in `redux`. The router-specific state is component-state in the `Router`. You can unify it all in `redux` with [`react-router-redux`](https://github.com/ReactTraining/react-router/tree/master/packages/react-router-redux). You'll need to apply some middleware, combine `routerReducer` with the rest of your reducers, and then swap out your `BrowserRouter` with a `ConnectedRouter`. You can read about the details [here](https://github.com/ReactTraining/react-router/tree/master/packages/react-router-redux). ================================================ FILE: react/test-files-in-create-react-app.md ================================================ # Test Files In create-react-app Any `.js` files placed in the `__tests__` directory will be treated as tests by Jest when running `yarn test`. If you don't want to place all of your files in that directory and especially if you want to co-located your test files with the source files, you can name them with the `.test.js` or `.spec.js` suffixes. Any files in your create-react-app project ending in these suffixes will be treated by Jest as test files and included in test runs. There are [more details](https://github.com/facebook/create-react-app/blob/master/packages/react-scripts/template/README.md#running-tests) in the docs. ================================================ FILE: react/test-that-element-does-not-render-in-the-component.md ================================================ # Test That Element Does Not Render In The Component With [`react-testing-library`](https://testing-library.com/docs/react-testing-library/intro), you can render a component and make assertions about the different parts of the component that get rendered. You can also make assertions that certain things _don't_ get rendered. To do that, first render the component: ```javascript import { render, screen } from '@testing-library/react' import '@testing-library/jest-dom/extend-expect' import MyComponent from '../MyComponent' test('renders component without Click Me button', () => { render() }) ``` Then add a `not` expectation with a `query*`-style matcher: ```javascript expect(screen.queryByText('Click Me')).not.toBeInTheDocument() ``` You'll get an immediate test failure if you try to directly select for the element using a `get*`-style matcher: ```javascript // ❌ will fail on `getByText` before the rest of the // assertion can be evaluated. expect(screen.getByText('Click Me')).not.toBeInTheDocument() ``` [source](https://kentcdodds.com/blog/common-mistakes-with-react-testing-library#using-query-variants-for-anything-except-checking-for-non-existence) ================================================ FILE: react/trigger-effect-only-when-the-component-mounts.md ================================================ # Trigger Effect Only When The Component Mounts With `useEffect`, you generally want to values used in the function to also be included in the _dependency list_. This ensures the effect is triggered whenever any of those values change. But what if you only want the effect to be triggered when the component first mounts? This can be done by including an _empty_ dependency list. ```javascript import React, { useState, useEffect } from "react"; function App() { const [count, setCount] = useState(0); const incrementCount = () => { setCount(prevCount => prevCount + 1); }; useEffect(() => { console.log("The count is:", count); }, []); return (

    Count: {count}

    ); } ``` In this example, we will see `The count is: 0` get logged when the component first mounts. As we hit the button to increment the count, nothing else will be logged. See the [live example](https://codesandbox.io/s/mystifying-currying-l2rw2). ================================================ FILE: react/update-formik-initial-values-when-props-change.md ================================================ # Update Formik Initial Values When Props Change When a [Formik](https://jaredpalmer.com/formik/) form mounts, whatever the initial values are set to is what they will be. Even if the initial values are computed from props, those props changing will not affect `initialValues` after mount. ```javascript const ZipForm = ({ currentZip }) => { return ( { // do stuff }} ... ``` If we are fetching the user's saved zip code asynchronously from a server while the form is first being rendered, then `currentZip` will start as an empty value. Once the async request comes back and `currentZip` is set, we won't see the form update the `zip` field. There was a time when you would have to jump through some hoops to make sure the freshest prop value made it into the form. Now, Formik provides a handier mechanism -- the `enableReinitialize` prop. ```javascript const ZipForm = ({ currentZip }) => { return ( { // do stuff }} ... ``` By setting `enableReinitialize` to true, we are telling Formik that any prop changes that flow into the `initialValues` object should cause those values to be _reinitialized_. See a [live example](https://codesandbox.io/s/sad-mendeleev-4dbbp). ================================================ FILE: react/upgrading-to-the-latest-react-in-codesandbox.md ================================================ # Upgrading To The Latest React In CodeSandbox At the time of writing this, the latest version of React is 16.3.1. Opening up [CodeSandbox](https://codesandbox.io/) and starting a new React project has us working with React 16.2. ![Defaults to React 16.2](https://i.imgur.com/AmgyfGc.png) By clicking on the _circular arrow_ upgrade icon next to `react` and `react-dom`, we will have upgraded each to 16.3.1. ![Upgrade to React 16.3.1](https://i.imgur.com/0DPLOY2.png) ================================================ FILE: react/use-a-ref-to-autofocus-an-input.md ================================================ # Use A Ref To Autofocus An Input When creating highly interactive interfaces with React, we are trying to make the user's experience of our app as smooth as possible. This means that when an edit button reveals an input field, we want that field to be in focus so that the user can immediately start typing. This is a great use for React's `ref` prop. When you supply your component with a function as the `ref` prop, that function will be called with a reference to itself on mount and with `null` on unmount. ```javascript class MyAutofocusInput extends React.Component { focusInput = (component) => { if (component) { component.focus(); } }; render() { return ( ); } } ``` When this component gets rendered, the input will be focused via our `focusInput` function. Note: refs only work with class components, so don't try to use it with a functional component. See [Refs and the DOM](https://reactjs.org/docs/refs-and-the-dom.html) in React's documentation for more details. ================================================ FILE: react/use-react-16-with-gatsby.md ================================================ # Use React 16 With Gatsby [Gatsby](https://www.gatsbyjs.org/), the blazing fast static site generator for React, is tied to React 15.6. If you've been using React 16+ for a while, then this may come as a bit of a buzzkill. Fortunately, there is a Gatsby plugin that let's you use React 16 with a Gatsby v1 site -- [gatsby-plugin-react-next](https://github.com/gatsbyjs/gatsby/tree/master/packages/gatsby-plugin-react-next). Add it the plugin as a dependency: ```bash $ yarn add gatsby-plugin-react-next ``` Then add it to the list of plugins in `gatsby-config.js`: ```javascript plugins: [`gatsby-plugin-react-next`]; ``` [source](https://twitter.com/gatsbyjs/status/990806495959826432) ================================================ FILE: react/use-withrouter-to-pass-down-react-router-history.md ================================================ # Use withRouter To Pass Down React-Router History A standard way to navigate with [react-router](https://github.com/ReactTraining/react-router) besides using the `Link` component is to call `history.push`. Components that are directly rendered by a `Route` will have access to this and other router props. But what about other components? The `withRouter` HOC gives us direct access to a `history` prop. ```javascript import React from 'react'; import { withRouter } from 'react-router'; const SpecialButton = withRouter(({ history, path, text }) => { return ( ) }); ``` This special button component is given the `history` prop via the `withRouter` HOC along with any props that we directly pass it. With that we are able to directly invoke a route change using `history.push()`. ================================================ FILE: react/visually-select-a-react-element-for-inspection.md ================================================ # Visually Select A React Element For Inspection Similar to the _Elements_ tab of Chrome devtools, the [React devtools extension](https://github.com/facebook/react-devtools) provides a visual element selector to make it easier to inspect an element you can see in the browser. ![select and inspect a react component](https://i.imgur.com/cGgSZfN.gif) Open the React devtools, click the crosshair icon, hover around the browser until the element you are looking for is visually highlighted, and then click. The React component hierarchy will be expanded to reveal that element. You can now inspect it or quickly navigate to nearby elements. ================================================ FILE: react/who-is-your-favorite-child.md ================================================ # Who Is Your Favorite Child? When we put some content inside the open and close tags of one of our components, we get access to it as the `children` prop. ```javascript const Parent = ({children}) => { return (

    These are my favorites:

    {children}
    ); } const App = () => (
    Greg and Marsha
    ); ``` What happens if we also provide an explicit `children` prop to `Parent`? ```javascript const App = () => (
    Greg and Marsha
    ); ``` Which will take precedence when we destructure `children` in the parent component? In the example above, we'll still see `Greg and Marsha` rendered. The content placed inside the tags will take precedence over the explicit `children` prop. See a [live example here](https://codesandbox.io/s/kmo5lk2lr5). ================================================ FILE: react/wrap-the-root-of-a-gatsby-app-in-a-component.md ================================================ # Wrap The Root Of A Gatsby App In A Component Each component that is defined in the `pages` directory of a [Gatsby](https://www.gatsbyjs.org/) app will be generated into a separate static page. Each of these pages is meant to stand on its own. Nevertheless, there is still a behind-the-scenes root component above all of these pages. There are cases where'd you like to wrap this root component with some other component, such as a Redux `Provider`. This can be done using the `wrapRootElement` hook from the Browser API in the `gatsby-browser.js` file. ```javascript // gatsby-browser.js import React from 'react'; import { Provider } from 'react-redux'; import store from './src/store'; export const wrapRootElement = ({ element }) => { return ( {element} ); } ``` Each page and each component in your Gatsby app will now be downstream from a Redux provider meaning that they can connect to the Redux store as needed. You can use this technique for any top-level component that need to be wrapped around the entire app. [source](https://www.gatsbyjs.org/docs/browser-apis/#wrapRootElement) ================================================ FILE: react-testing-library/check-that-a-component-renders-as-null.md ================================================ # Check That A Component Renders As Null Consider a component that sometimes renders as `null`. ```javascript const HiddenMessage = ({ message, hidden }) => { if (hidden) return null; return {message}; }; ``` How can we test the version of this component that renders as `null` when `hidden` is `true`? When [react-testing-library](https://testing-library.com/docs/react-testing-library/intro) renders a component, it wraps the whole thing in a surrounding `
    `. Knowing this, we can check if a component renders to `null` by checking the contents of the wrapping `
    ` container. ```javascript import React from "react"; import { render } from "@testing-library/react"; import "@testing-library/jest-dom/extend-expect"; test("renders as null", () => { const { container } = render(