mirror of
https://github.com/jbranchaud/til
synced 2026-07-07 09:30:34 +00:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| ae2c7aa028 |
@@ -10,7 +10,7 @@ pairing with smart people at Hashrocket.
|
|||||||
|
|
||||||
For a steady stream of TILs, [sign up for my newsletter](https://crafty-builder-6996.ck.page/e169c61186).
|
For a steady stream of TILs, [sign up for my newsletter](https://crafty-builder-6996.ck.page/e169c61186).
|
||||||
|
|
||||||
_1589 TILs and counting..._
|
_1562 TILs and counting..._
|
||||||
|
|
||||||
See some of the other learning resources I work on:
|
See some of the other learning resources I work on:
|
||||||
- [Ruby Operator Lookup](https://www.visualmode.dev/ruby-operators)
|
- [Ruby Operator Lookup](https://www.visualmode.dev/ruby-operators)
|
||||||
@@ -199,15 +199,12 @@ See some of the other learning resources I work on:
|
|||||||
- [Check The Status of All Services](devops/check-the-status-of-all-services.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)
|
- [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)
|
- [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)
|
- [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)
|
- [Path Of The Packets](devops/path-of-the-packets.md)
|
||||||
- [Push Non-master Branch To Heroku](devops/push-non-master-branch-to-heroku.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)
|
- [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)
|
- [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)
|
- [Running Out Of inode Space](devops/running-out-of-inode-space.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)
|
- [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)
|
- [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)
|
- [Wipe A Heroku Postgres Database](devops/wipe-a-heroku-postgres-database.md)
|
||||||
@@ -307,7 +304,6 @@ See some of the other learning resources I work on:
|
|||||||
- [Configuring The Pager](git/configuring-the-pager.md)
|
- [Configuring The Pager](git/configuring-the-pager.md)
|
||||||
- [Copy A File From Another Branch](git/copy-a-file-from-another-branch.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 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)
|
- [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)
|
- [Delete All Untracked Files](git/delete-all-untracked-files.md)
|
||||||
- [Determine The Hash Id For A Blob](git/determine-the-hash-id-for-a-blob.md)
|
- [Determine The Hash Id For A Blob](git/determine-the-hash-id-for-a-blob.md)
|
||||||
@@ -338,7 +334,6 @@ See some of the other learning resources I work on:
|
|||||||
- [Interactively Unstage Changes](git/interactively-unstage-changes.md)
|
- [Interactively Unstage Changes](git/interactively-unstage-changes.md)
|
||||||
- [Keep File Locally With `git rm`](git/keep-file-locally-with-git-rm.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)
|
- [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 Files Changed Between Two Branches](git/list-all-files-changed-between-two-branches.md)
|
||||||
- [List Branches That Contain A Commit](git/list-branches-that-contain-a-commit.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 Commits On A Branch](git/list-commits-on-a-branch.md)
|
||||||
@@ -356,7 +351,6 @@ See some of the other learning resources I work on:
|
|||||||
- [Quicker Commit Fixes With The Fixup Flag](git/quicker-commit-fixes-with-the-fixup-flag.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)
|
- [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 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)
|
- [Remove Untracked Files From A Directory](git/remove-untracked-files-from-a-directory.md)
|
||||||
- [Rename A Remote](git/rename-a-remote.md)
|
- [Rename A Remote](git/rename-a-remote.md)
|
||||||
- [Renaming A Branch](git/renaming-a-branch.md)
|
- [Renaming A Branch](git/renaming-a-branch.md)
|
||||||
@@ -408,7 +402,6 @@ See some of the other learning resources I work on:
|
|||||||
- [Disable A Workflow With The gh CLI](github-actions/disable-a-workflow-with-the-gh-cli.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)
|
- [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)
|
- [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
|
### Go
|
||||||
|
|
||||||
@@ -671,7 +664,6 @@ See some of the other learning resources I work on:
|
|||||||
- [Set A Window To Its Default Zoom Level](mac/set-a-window-to-its-default-zoom-level.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)
|
- [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)
|
- [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 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)
|
- [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)
|
- [View All Windows Of The Current App](mac/view-all-windows-of-the-current-app.md)
|
||||||
@@ -784,7 +776,6 @@ See some of the other learning resources I work on:
|
|||||||
- [Convert A String To A Timestamp](postgres/convert-a-string-to-a-timestamp.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 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 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)
|
- [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 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 Composite Primary Key](postgres/create-a-composite-primary-key.md)
|
||||||
@@ -902,14 +893,12 @@ See some of the other learning resources I work on:
|
|||||||
- [Timestamp Functions](postgres/timestamp-functions.md)
|
- [Timestamp Functions](postgres/timestamp-functions.md)
|
||||||
- [Toggling The Pager In PSQL](postgres/toggling-the-pager-in-psql.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)
|
- [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 All Rows](postgres/truncate-all-rows.md)
|
||||||
- [Truncate Tables With Dependents](postgres/truncate-tables-with-dependents.md)
|
- [Truncate Tables With Dependents](postgres/truncate-tables-with-dependents.md)
|
||||||
- [Turning Timing On](postgres/turn-timing-on.md)
|
- [Turning Timing On](postgres/turn-timing-on.md)
|
||||||
- [Two Ways To Compute Factorial](postgres/two-ways-to-compute-factorial.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)
|
- [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)
|
- [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)
|
- [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 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 A Trigger To Mirror Inserts To Another Table](postgres/use-a-trigger-to-mirror-inserts-to-another-table.md)
|
||||||
@@ -950,7 +939,6 @@ See some of the other learning resources I work on:
|
|||||||
- [Add A Check Constraint To A Table](rails/add-a-check-constraint-to-a-table.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 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 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 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 ActiveRecord Error Not Tied To Any Attribute](rails/add-activerecord-error-not-tied-to-any-attribute.md)
|
||||||
- [Add React With Webpacker To A New Rails App](rails/add-react-with-webpacker-to-a-new-rails-app.md)
|
- [Add React With Webpacker To A New Rails App](rails/add-react-with-webpacker-to-a-new-rails-app.md)
|
||||||
@@ -963,7 +951,6 @@ See some of the other learning resources I work on:
|
|||||||
- [All or Nothing Database Transactions](rails/all-or-nothing-database-transactions.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)
|
- [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)
|
- [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)
|
- [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)
|
- [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 Getter without the Recursion](rails/attribute-getter-without-the-recursion.md)
|
||||||
@@ -995,7 +982,6 @@ See some of the other learning resources I work on:
|
|||||||
- [Define The Root Path For The App](rails/define-the-root-path-for-the-app.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)
|
- [Delete Paranoid Records](rails/delete-paranoid-records.md)
|
||||||
- [Demodulize A Class Name](rails/demodulize-a-class-name.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)
|
- [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)
|
- [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)
|
- [Empty find_by Returns First Record](rails/empty-find-by-returns-first-record.md)
|
||||||
@@ -1021,7 +1007,6 @@ See some of the other learning resources I work on:
|
|||||||
- [Hash Slicing](rails/hash-slicing.md)
|
- [Hash Slicing](rails/hash-slicing.md)
|
||||||
- [Ignore Poltergeist JavaScript Errors](rails/ignore-poltergeist-javascript-errors.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)
|
- [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)
|
- [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)
|
- [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 All Installable Rails Versions](rails/list-all-installable-rails-versions.md)
|
||||||
@@ -1041,7 +1026,6 @@ See some of the other learning resources I work on:
|
|||||||
- [Migrating Up Down Up](rails/migrating-up-down-up.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)
|
- [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)
|
- [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)
|
|
||||||
- [Params Includes Submission Button Info](rails/params-includes-submission-button-info.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)
|
- [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 Query Params From A URL](rails/parse-query-params-from-a-url.md)
|
||||||
@@ -1070,7 +1054,6 @@ See some of the other learning resources I work on:
|
|||||||
- [Run A Rake Task Programmatically](rails/run-a-rake-task-programmatically.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 Commands With Specific Rails Version](rails/run-commands-with-specific-rails-version.md)
|
||||||
- [Run Some Code Whenever Rails Console Starts](rails/run-some-code-whenever-rails-console-starts.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)
|
- [Schedule Sidekiq Jobs Out Into The Future](rails/schedule-sidekiq-jobs-out-into-the-future.md)
|
||||||
- [Secure Passwords With Rails And Bcrypt](rails/secure-passwords-with-rails-and-bcrypt.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 Select By Selector](rails/select-a-select-by-selector.md)
|
||||||
@@ -1081,7 +1064,6 @@ See some of the other learning resources I work on:
|
|||||||
- [Set DateTime To Include Time Zone In Migrations](rails/set-datetime-to-include-time-zone-in-migrations.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 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 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 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 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)
|
- [Set The Default Development Port](rails/set-the-default-development-port.md)
|
||||||
@@ -1281,7 +1263,6 @@ See some of the other learning resources I work on:
|
|||||||
- [Exit A Process With An Error Message](ruby/exit-a-process-with-an-error-message.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)
|
- [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 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)
|
- [FactoryGirl Sequences](ruby/factory-girl-sequences.md)
|
||||||
- [Fail](ruby/fail.md)
|
- [Fail](ruby/fail.md)
|
||||||
- [Fetch Warns About Superseding Block Argument](ruby/fetch-warns-about-superseding-block-argument.md)
|
- [Fetch Warns About Superseding Block Argument](ruby/fetch-warns-about-superseding-block-argument.md)
|
||||||
@@ -1500,7 +1481,6 @@ See some of the other learning resources I work on:
|
|||||||
- [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 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 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 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 File Descriptor with Process Substitution](unix/create-a-file-descriptor-with-process-substitution.md)
|
||||||
- [Create A Sequence Of Values With A Step](unix/create-a-sequence-of-values-with-a-step.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)
|
- [Curl With Cookies](unix/curl-with-cookies.md)
|
||||||
@@ -1537,7 +1517,6 @@ See some of the other learning resources I work on:
|
|||||||
- [Get Matching Filenames As Output From Grep](unix/get-matching-filenames-as-output-from-grep.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 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 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)
|
- [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 All Directories In Zsh](unix/globbing-for-all-directories-in-zsh.md)
|
||||||
- [Globbing For Filenames In Zsh](unix/globbing-for-filenames-in-zsh.md)
|
- [Globbing For Filenames In Zsh](unix/globbing-for-filenames-in-zsh.md)
|
||||||
@@ -1556,8 +1535,6 @@ See some of the other learning resources I work on:
|
|||||||
- [Killing A Frozen SSH Session](unix/killing-a-frozen-ssh-session.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)
|
- [Last Argument Of The Last Command](unix/last-argument-of-the-last-command.md)
|
||||||
- [Less With Style](unix/less-with-style.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 The Enabled ZSH Options](unix/list-all-the-enabled-zsh-options.md)
|
||||||
- [List All Users](unix/list-all-users.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 In A Single Column](unix/list-files-in-a-single-column.md)
|
||||||
@@ -1827,7 +1804,6 @@ See some of the other learning resources I work on:
|
|||||||
- [Break Justfile Into Separate Hidden Steps](workflow/break-justfile-into-separate-hidden-steps.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)
|
- [Change Window Name In iTerm](workflow/change-window-name-in-iterm.md)
|
||||||
- [Configure Email Redirect With Cloudflare](workflow/configure-email-redirect-with-cloudflare.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)
|
- [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 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 A Public URL For A Local Server](workflow/create-a-public-url-for-a-local-server.md)
|
||||||
@@ -1838,15 +1814,12 @@ See some of the other learning resources I work on:
|
|||||||
- [Import A Github Project Into CodeSandbox](workflow/import-a-github-project-into-codesandbox.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)
|
- [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)
|
- [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)
|
- [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)
|
- [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)
|
- [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 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)
|
- [Set Recurring Reminders In Slack](workflow/set-recurring-reminders-in-slack.md)
|
||||||
- [Show Linting Errors In Zed](workflow/show-linting-errors-in-zed.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)
|
- [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)
|
- [Update asdf Plugins With Latest Package Versions](workflow/update-asdf-plugins-with-latest-package-versions.md)
|
||||||
- [View The PR For The Current GitHub Branch](workflow/view-the-pr-for-the-current-github-branch.md)
|
- [View The PR For The Current GitHub Branch](workflow/view-the-pr-for-the-current-github-branch.md)
|
||||||
|
|||||||
@@ -1,28 +0,0 @@
|
|||||||
# 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.
|
|
||||||
@@ -1,44 +0,0 @@
|
|||||||
# 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)
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
# 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.
|
|
||||||
@@ -1,48 +0,0 @@
|
|||||||
# 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.
|
|
||||||
@@ -1,49 +0,0 @@
|
|||||||
# 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}'
|
|
||||||
```
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
# 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 chuncks 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 ...
|
|
||||||
```
|
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
# 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.
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
# 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/)
|
|
||||||
@@ -1,56 +0,0 @@
|
|||||||
# 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)
|
|
||||||
@@ -1,58 +0,0 @@
|
|||||||
# 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.
|
|
||||||
@@ -1,45 +0,0 @@
|
|||||||
# 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).
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
# 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/)
|
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
# 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
|
|
||||||
<div class="max-w-3xl mx-auto">
|
|
||||||
<div class="space-y-4">
|
|
||||||
<div class="prose mt-8 text-gray-700">
|
|
||||||
<%= @record.notes %>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
```
|
|
||||||
|
|
||||||
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
|
|
||||||
<div class="max-w-3xl mx-auto">
|
|
||||||
<div class="space-y-4">
|
|
||||||
<div class="prose mt-8 text-gray-700">
|
|
||||||
<%= simple_format(@record.notes) %>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
```
|
|
||||||
|
|
||||||
This turns single `\n` characters into a `<br />` tag and double `\n\n` cause
|
|
||||||
the surrounding paragraphs to be wrapped in `<p>` tags. That simple formatting
|
|
||||||
combined with my existing TailwindCSS styles makes the formatting of my text
|
|
||||||
immediately look much better.
|
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
# 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).
|
|
||||||
@@ -1,45 +0,0 @@
|
|||||||
# 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)
|
|
||||||
@@ -1,38 +0,0 @@
|
|||||||
# 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 %>
|
|
||||||
```
|
|
||||||
@@ -1,44 +0,0 @@
|
|||||||
# 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)
|
|
||||||
@@ -1,52 +0,0 @@
|
|||||||
# 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
|
|
||||||
<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<!-- ... -->
|
|
||||||
|
|
||||||
<meta
|
|
||||||
property="og:description"
|
|
||||||
content="<%= content_for?(:description) ? yield(:description) : 'Default description' %>"
|
|
||||||
>
|
|
||||||
|
|
||||||
<!-- ... -->
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<!-- ... -->
|
|
||||||
</html>
|
|
||||||
```
|
|
||||||
|
|
||||||
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 `<head>`, 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.
|
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
# Extract Capture Group Matches With String Slices
|
|
||||||
|
|
||||||
Ruby's _string slice_ syntax allows us to use the square brackets to access
|
|
||||||
portions of a string. It's most common to pass positional integer index
|
|
||||||
arguments or a range. However, in true Ruby fashion, another way of thinking
|
|
||||||
about defining the slice of a string is based on a regex match.
|
|
||||||
|
|
||||||
We can pass a regex and an int (specifying which match we want) to extract some
|
|
||||||
portion of a string based on the regex match. That includes capture groups.
|
|
||||||
|
|
||||||
Here are a couple examples of extracting matching capture groups as well as
|
|
||||||
getting the entire regex match:
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
> "me+abc123@email.com"[/.+\+(.+)@(.+)/, 1]
|
|
||||||
=> "abc123"
|
|
||||||
|
|
||||||
> "me+abc123@email.com"[/.+\+(.+)@(.+)/, 2]
|
|
||||||
=> "email.com"
|
|
||||||
|
|
||||||
> "me+abc123@email.com"[/.+\+(.+)@(.+)/, 0]
|
|
||||||
=> "me+abc123@email.com"
|
|
||||||
|
|
||||||
> "me+abc123@email.com"[/.+\+(.+)@(.+)/]
|
|
||||||
=> "me+abc123@email.com"
|
|
||||||
```
|
|
||||||
|
|
||||||
The `0`th match (which is the default) corresponds to the full match. Each
|
|
||||||
integer position after that corresponds to any capture groups. This maps
|
|
||||||
directly to the underlying `MatchData` object:
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
> /.+\+(.+)@(.+)/.match("me+abc123@email.com")
|
|
||||||
=> #<MatchData "me+abc123@email.com" 1:"abc123" 2:"email.com">
|
|
||||||
```
|
|
||||||
|
|
||||||
[source](https://ruby-doc.org/3.3.6/String.html#class-String-label-String+Slices)
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
# Count The Number Of Words On A Webpage
|
|
||||||
|
|
||||||
I was reading through a couple sections of the `postfix` documentation and I
|
|
||||||
was astounded at how large the webpage is, and that is just for the `main.cf`
|
|
||||||
file format.
|
|
||||||
|
|
||||||
Curiosity got the best of me and I wanted to get a sense of the magnitude of
|
|
||||||
the page. A word count seemed like a good measure.
|
|
||||||
|
|
||||||
Using `pandoc` and a couple other unix utilities, I was able to quickly get
|
|
||||||
that number.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
curl -s http://www.postfix.org/postconf.5.html\#virtual_mailbox_maps | pandoc -f html -t plain | wc -w
|
|
||||||
88383
|
|
||||||
```
|
|
||||||
|
|
||||||
Generically, that is:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
curl -s url | pandoc -f html -t plain | wc -w
|
|
||||||
```
|
|
||||||
|
|
||||||
Pandoc produces a plain-text version of the HTML page that was pulled in by
|
|
||||||
`curl` and then we use `wc` to get a word (`-w`) count.
|
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
# Get Word Count For All Files In Git Repo
|
|
||||||
|
|
||||||
As part of gathering numbers for [A Decade of TILs](), I wanted to get an word
|
|
||||||
count of all the TIL markdown files I've committed to this project over its 10
|
|
||||||
year history. By using `git ls-files` with a pattern, I can get a list of all
|
|
||||||
file names. Then with `xargs` I can pass that entire list to `wc -w` which
|
|
||||||
gives a word count of each. The final line that `wc -w` outputs is a sum total
|
|
||||||
of all the file word counts. Lastly, piping that through `tail -n1` gives me
|
|
||||||
just that last total count line.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ git ls-files "*/**.md" | xargs wc -w | tail -n1
|
|
||||||
206816 total
|
|
||||||
```
|
|
||||||
|
|
||||||
Since the `tail -n1` obfuscates what the `wc -w` is doing, here is what that
|
|
||||||
looks like before that final pipe.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ git ls-files "*/**.md" | tail -n3 | xargs wc -w
|
|
||||||
115 zsh/add-to-the-path-via-path-array.md
|
|
||||||
190 zsh/link-a-scalar-to-an-array.md
|
|
||||||
214 zsh/use-a-space-to-exclude-command-from-history.md
|
|
||||||
519 total
|
|
||||||
```
|
|
||||||
|
|
||||||
I can even clean up the final output a bit more with `awk`:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ git ls-files "*/**.md" | xargs wc -w | tail -n1 | awk '{print $1}'
|
|
||||||
206816
|
|
||||||
```
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
# Limit Protocols Used In A cURL Command
|
|
||||||
|
|
||||||
I was about to install [`atuin`](https://github.com/atuinsh/atuin). I went to
|
|
||||||
their _Quick Start_ section to grab whatever command I would need to install
|
|
||||||
it. It was a `curl` statement piped to `sh`. The thing that caught my attention
|
|
||||||
though was I `curl` flag that I didn't recognize — `--proto`.
|
|
||||||
|
|
||||||
> Tells curl to limit what protocols it may use for transfers.
|
|
||||||
|
|
||||||
Using `curl --proto '=https' ...` we can enforce that only an `https` URL can
|
|
||||||
be used in this command.
|
|
||||||
|
|
||||||
Here is what happens if I try to run the `atuin`-provided `curl` command after
|
|
||||||
I have downgraded their URL to be `http`:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
curl --proto '=https' --tlsv1.2 -LsSf http://setup.atuin.sh | sh
|
|
||||||
curl: (1) Protocol "http" not supported or disabled in libcurl
|
|
||||||
```
|
|
||||||
|
|
||||||
It doesn't even attempt the request. The protocol is considered unsupported and
|
|
||||||
the command immediately fails.
|
|
||||||
|
|
||||||
In addition to only installing software we trust, we should make sure we are
|
|
||||||
only doing so over a protocol we trust (namely, `https`).
|
|
||||||
|
|
||||||
See `man curl` for more details, including about the modifiers (`=`, `+`, `-`).
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
# List All Fonts On Your Machine
|
|
||||||
|
|
||||||
In trying to figure out what _FiraCode_ font I have installed on my machine
|
|
||||||
and what it is called, I came across [this StackOverflow
|
|
||||||
answer](https://stackoverflow.com/a/52789662/535590) which shares the
|
|
||||||
following one-liner:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ fc-list | awk '{$1=""}1' | cut -d: -f1 | sort | uniq
|
|
||||||
```
|
|
||||||
|
|
||||||
This uses `fc-list` to get the names of all the fonts available on your
|
|
||||||
machine. This seems to work on both Linux and Mac. Through a series of `awk`,
|
|
||||||
`cut`, and `sort | uniq`, this command produces a clean, easily-browsed list
|
|
||||||
of fonts.
|
|
||||||
|
|
||||||
I like to take this a step further by piping it all to `fzf` where I can then
|
|
||||||
narrow down the output to just lines that match _FiraCode_.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ fc-list | awk '{$1=""}1' | cut -d: -f1 | sort | uniq | fzf
|
|
||||||
```
|
|
||||||
|
|
||||||
See also [`system_profiler SPFontsDataType`](https://apple.stackexchange.com/questions/35852/list-of-activated-fonts-with-shell-command-in-os-x/243746#243746).
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
# Control Media With Drop Keyboard
|
|
||||||
|
|
||||||
I have a [Drop CTRL](https://drop.com/buy/drop-ctrl-v2-mechanical-keyboard)
|
|
||||||
mechanical keyboard which mostly works like any other keyboard. It also has a
|
|
||||||
set of functionality that can be accessed via the `fn` (function) key. The
|
|
||||||
function key can be used to configure the keyboard's LEDs, but I tend to set
|
|
||||||
and forget that.
|
|
||||||
|
|
||||||
Instead, I like to use the function key to control media. That is, adjust the
|
|
||||||
volume, play and pause, and skip to the next song.
|
|
||||||
|
|
||||||
Here is a listing of the ones I use:
|
|
||||||
|
|
||||||
- `Fn + Insert` to Pause / Play
|
|
||||||
- `Fn + PgUp/PgDown` to Increase / Decrease the volume
|
|
||||||
- `Fn + Del/End` to go to the Previous / Next song
|
|
||||||
|
|
||||||
Here is a [full listing of the function
|
|
||||||
keys](https://drop.com/talk/9382/how-to-configure-your-drop-keyboard) for Drop
|
|
||||||
keyboards.
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
# Pop Videos Out As Picture-in-Picture
|
|
||||||
|
|
||||||
I recently learned that just about any video playing in Chrome and Firefox can
|
|
||||||
be popped out to a picture-in-picture (PIP) player. A PIP player gives you a sidecar
|
|
||||||
video player window that you can arrange and resize anywhere on your screen. It
|
|
||||||
sits on top of other windows so that you can view it while working from other
|
|
||||||
apps. This is useful if, for instance, you are working through a coding
|
|
||||||
tutorial on youtube.
|
|
||||||
|
|
||||||
For most video players, you can right click on the video and the menu that
|
|
||||||
appears will include a "Picture in Picture" option. Select that and the arrange
|
|
||||||
the player to your liking.
|
|
||||||
|
|
||||||
Youtube overrides right-click. If you right-click, you'll see one menu of
|
|
||||||
Youtube-specific options. Right-click a second time to open the standard
|
|
||||||
browser menu which will include the PIP option.
|
|
||||||
|
|
||||||
I noticed while also testing this on Firefox, that they have a PIP icon that
|
|
||||||
appears as a small overlay on the right side of the video player that you can
|
|
||||||
click as well. This is useful because I found some site's video players were
|
|
||||||
(inadvertently) preventing right-click.
|
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
# Send A PDF To Your Kindle
|
|
||||||
|
|
||||||
I recently got a Kindle. I already have a bunch of PDF and ePub books on my
|
|
||||||
computer that I've bought over the years. I wanted to be able to read some of
|
|
||||||
those books on the Kindle. I found that there is a way to send these formats to
|
|
||||||
your Kindle via email.
|
|
||||||
|
|
||||||
There are a couple steps to get this working.
|
|
||||||
|
|
||||||
First, from the Amazon account that is tied to the Kindle device, open the
|
|
||||||
_Account_ dropdown and click _Devices. Any devices tied to your account will be
|
|
||||||
listed there. Navigate to the one you want to send to. Under the _Device
|
|
||||||
Summary_ with be a custom email address for that device. Something like
|
|
||||||
`youremail_abc123@kindle.com`.
|
|
||||||
|
|
||||||
That's the email you'll send the PDF or ePub attachment to.
|
|
||||||
|
|
||||||
Second, that Kindle email address will only receive and process documents from
|
|
||||||
a known, verified email address. Back on the _Devices_page, click on the
|
|
||||||
_Preferences_ tab. Under _Personal Document Preferences_ make sure that the
|
|
||||||
_Approved Personal Document Email List_ includes the email address you'll be
|
|
||||||
sending from. Add it if not.
|
|
||||||
|
|
||||||
Everything is set up. Now compose an email to that Kindle address, add the
|
|
||||||
attachment, and send. Give it 5 or so minutes to process and it should show up
|
|
||||||
on your device.
|
|
||||||
|
|
||||||
Additionally, you can go to the _Content_ tab and then to _Digital Content_ to
|
|
||||||
see what documents you have set and which devices have received them.
|
|
||||||
|
|
||||||
[source](https://goodereader.com/blog/kindle/here-is-how-you-can-read-pdf-files-on-the-amazon-kindle)
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
# Temporarily Hide CleanShot X Capture Previews
|
|
||||||
|
|
||||||
The _capture previews_ that CleanShot X provides are a useful part of my
|
|
||||||
workflow. I often capture a screenshot or recording a bit in advance of needing
|
|
||||||
to add it as, say, an attachment. The preview icons float off to the right of
|
|
||||||
my screen, generally out of the way. As soon as I need them, I can annotate,
|
|
||||||
drag-n-drop, etc.
|
|
||||||
|
|
||||||
Sometimes, however, they do get in the way. But I'm not ready to dismiss them
|
|
||||||
and I don't want to save them off to some folder buried in my file system.
|
|
||||||
|
|
||||||
To temporarily slide the capture previews down off the screen, I can hit the
|
|
||||||
shortcut `Ctrl+Opt+Cmd+H`. The arrow at the butto of the screen can be clicked
|
|
||||||
to unhide them, or I can hit the same shortcut sequence to reveal them.
|
|
||||||
Reference in New Issue
Block a user