Transform to Camel Case with VS Code
Using VS code's command palette, Cmd+Shift+P, You can transform highlighted text to camel case
You can also transform to most other cases.
Using VS code's command palette, Cmd+Shift+P, You can transform highlighted text to camel case
You can also transform to most other cases.
The scrollHeight
Element property gives you an elements entire scrollable content height, meaning it accounts for the overflowed content not visible on the screen.
You can run rails stats
to print out statistics around lines of code,
classes, methods, code to test ratio, etc for your rails app. It'll print out a
table like this:
(Don't mind the code to test ratio...it's a proof of concept app 😅)
h/t Joel Hawksley, I learned about this from the excellent talk he gave at Chicago Ruby last week.
Today I learned you can write nested expectations in rspec
. I find this is approach useful with writing request-style tests where I want to ensure the request returned successfully and also ensure the expected effects happened.
it "creates the post" do
expect {
expect(request.response_code).to eq(200)
}.to change { Post.count }.by(1)
end
If either the response is not 200
, or the Post
count doesn't change, then the test fails.
There is a gotcha that I run into when I build these tests iteratively - first the inner expectation on it's own, wrap it in the outer block, and then add the outer matcher. If you wrap the inner request in an expect block, but don't have any assertions on that block, it will always pass - because we're not matching against anything.
RSpec.describe do
# ❌ - as expected
it do
expect(true).to eq(false)
end
# ❌ - as expected, outer expectation passes but inner fails, so test fails
it do
expect {
expect(true).to eq(false)
}.to not_change { 0 }
end
# ✅ - :-? watch out for this one, even though the inner expectation fails, test passes
it do
expect {
expect(true).to eq(false)
}
end
end
Ecto.Query has a function called exclude
you can call it to remove previous parts of a query that have already been defined
query = from(u in User,
where: u.email == "jack@example.com",
select: u.email
query
|> exclude(:where)
#=> will return the %Ecto.Query{} expression without the where clause
Enum.random
is a simple way to select a random element of an Elixir enum.
[1, 2, 3]
|> Enum.random()
# => who knows?? either 1, 2, or 3
Due to github permissions on projects that I am working on I wanted to switch which user I am connecting to github on a per project base. I also want to keep using ssh keys for that, so I found out that we can have 2 different ssh keys pointint to the same domain, we just had to do a simple twich:
Host github.com
HostName github.com
IdentityFile ~/.ssh/id_ed25519
Host vinny.github.com
HostName github.com
IdentityFile ~/.ssh/vinny_id_ed25519
So I end up creating 2 ssh keys, 1 per user that I need to access on github. Then left the original ssh key as regular github.com
domain, so no changes there, and on the projects that I need to access with the other user I had to clone with:
git clone git@vinny.github.com:MyOrganization/my-project.git
And if you have an exisintg project that you want to move to this new github user you'll have to change the remote on that:
git remote remove origin
git remote add origin git@vinny.github.com:MyOrganization/my-project.git
By the way, as you are changing which keys to use with an existing domain, you may have to restart the ssh-agent
in order for this to work:
eval "$(ssh-agent -s)"
Did you know that you can use pattern matching headers with anonymous functions in elixir? It works like this:
anon = fn
{:ok, value} -> "Got value: #{value}"
_ -> "Doing something else"
end
# IO.puts anon.({:ok, 42}) # => "Got value: 42"
# IO.puts anon.(:ok) # => "Doing something else"
We had a situation that we wanted to guarantee that an "index" page under a tab navigation was always there, this way we could go back using the navigation to the index page, even if the user entered to that tab navigation via another screen. I learned that we can use withAnchor
prop in conjunction with the unstable settings initialRouteName
. This worked really fine and now our back buttons always go to each tab's index screens.
Check this out Expo Router navigation patterns
You can make text unable to be highlighted by using the tailwind class select-none
<div class="select-none">
You can't highlight/select me
</div>
I'm on a bit of a random kick lately. Today I learned you can randomly sort rows returned in a query using Postgres' random()
function.
SELECT *
FROM users
ORDER BY random()
What it does is it calculates a random value for each row returned, and then orders by that calculated value. If the query returns a lot of rows this can be slow, so probably more useful for exploration or one-offs not necessarily for production uses.
When rescuing exceptions with the intent to reraise them after doing some logic, you don't need to pass the original exception to the raise
method:
begin
# ...
rescue
puts "Something went wrong"
raise # will raise the original exception
end
TIL about the RAILS_DEVELOPMENT_HOSTS
env variable. This adds the hosts specified to your environment, so that you don't have to manually update config.hosts
.
Just start your server like so
$ RAILS_DEVELOPMENT_HOSTS="my-url.ngrok-free.app" bin/rails s
h/t Matt Polito
Today I Learned about jot
, a command line utility that prints sequential or random data. Let's focus on the sequential bit.
jot
takes arguments for reps
, begin
and end
. So if we wanted to print the integers from 1 to 10, we'd do:
$ jot - 1 10
1
2
3
4
5
6
7
8
9
10
Which is neat, but only so cool. What if we wanted to print 21 evenly spaced numbers between -1 and 1? That would be cooler:
$ jot 21 -1 1.00 #the 1.00 tells the output to be float, not int
-1.00
-0.90
-0.80
-0.70
-0.60
-0.50
-0.40
-0.30
-0.20
-0.10
-0.00
0.10
0.20
0.30
0.40
0.50
0.60
0.70
0.80
0.90
1.00
You can even print all the ASCII characters with:
jot -c 128 0
1
2
3
4
5
6
7
8
9
:
;
<
=
>
?
@
A
B
C
...
Say you have a variable set in a shell script:
export testPath='/Users/mary/code/thingy.rb'
You can echo that variable as usual:
echo $testPath
# /Users/mary/code/thingy.rb
But you can also get substrings of that variable as you echo it, using substring extraction. The substring extraction syntax is ${variable:offset:length}
echo ${testPath:11}
# /code/thingy.rb
echo ${testPath:12:4}
# code
You can also change the prefix (${variable#prefix}
) and suffix (${variable%suffix}
):
echo ${testPath#/Users/mary/code/}
# thingy.rb
echo ${testPath%.rb}
# /Users/mary/code/thingy
You can sort
to sort text in a file or STDIN. But what if you want to embrace the chaos and sort randomly? Turns out, there's a few ways to do that.
sort -R file.txt
- sort
's -R
option does a random sort rather than lexicographically. The caveat here is if you have duplicates, they will be grouped together in the resulting output. shuf file.txt
- shuf
is a standalone utility to generate random permutations. Read their man pages to learn more!
easings.net has a handy table for visualizing easing functions for animation curves.
By default, if you make a change to a belongs_to
associated model and save the parent model, the associated model won't be saved. This is probably a good default, but it is overridable with the :autosave
option.
class Book < ApplicationRecord
belongs_to :author
end
book = Book.first
book.author.name # => "J.R.R. Tolkein"
book.author.name = "John Tolkein"
book.save
book.reload
book.author.name # => "J.R.R. Tolkein"
But we can change this behaviour by setting :autosave
to true.
class Book < ApplicationRecord
belongs_to :author, autosave: :true
end
book = Book.first
book.author.name # => "J.R.R. Tolkein"
book.author.name = "John Tolkein"
book.save
book.reload
book.author.name # => "John Tolkein"
While this is off by default for belongs_to
associations, has_one
and has_many
associations have :autosave
true because the foreign keys are on the associated records to ensure those FKs are kept up to date.
PostgreSQL has a function called jsonb_to_recordset
that will return your jsonb data as if it were rows in the database.
Say we have the following example table:
create table notes (title varchar, bullets jsonb);
insert into notes (title, bullets)
values ('Inspection', '[{"field": "Tires", "measured": "Tread Height", "value": "3mm"},{"field": "Brakes", "measured": "Caliper Boot", "value": "cracked"}]');
To use the jsonb_to_recordset
function, we can do the following:
select title, field, measured, value
from notes
cross join lateral jsonb_to_recordset(notes.bullets)
as temp(field varchar, measured varchar, value varchar);
The function gives us the following output:
title | field | measured | value
------------+--------+--------------+---------
Inspection | Tires | Tread Height | 3mm
Inspection | Brakes | Caliper Boot | cracked
One of the nice things about working with Visual Studio based text editors is its multi-cursor abilities. For those who don't know, you can highlight a word and pressing CMD
+D
you can place another cursor at the next occurrence of that string.
This is helpful for making a lot of changes at once. Here's an example where we can fix a typo that was made all throughout a file:
typo = getTypo
if(typo)
# do something with the typo
else
# do something else
end
def getTypo()
typo
end
Well, what if we want a cursor at every occurrence of a typo, but we want a faster way than pressing CMD
+D
a bunch of times?
TLDR
Just highlight the word you want to grab and press CMD
+Shift
+L
and it will grab all occurrences found in that file. (Just be careful and make sure that's what you actually want!)
In vim :e
edits the current file (reloads from disk), which is useful if the file has been changed outside of vim and you want to load those changes into your buffer in vim.
I ran into a scenario where I ran a formatter outside of vim, and wanted to reload all files open in buffers in my vim session. I took a guess that :ea
would edit all (like :wa
writes all, :qa
quits all) - but alas, swing and a miss.
The way to do this is with :bufdo e
. bufdo
executes the command - in this case e
- in each buffer in the buffer list.
Reading man pages in (n)vim is my new favorite way to read man pages. (n)vim gives you syntax highlighting, plus you can follow the links with Shift + K
(yes, there are links between man pages - I never knew!).
You can override the default pager for man
by setting the MANPAGER
environment variable (the default is less -sR
) - per the nvim help page, you can use nvim with export MANPAGER='nvim +Man!'
.
Happy manual reading!
h/t Josh Branchaud, the TIL GOAT.
I found out that Keyword.validate/2 is a great way to guarantee that a function would be called with the right keyword options. Check this out:
def do_something(%User{} = user, opts \\ []) when is_list(opts) do
{:ok, opts} = Keyword.validate(opts, [:preload])
preload = Keyword.get(opts, :preload, [])
#...
end
Here's some interesting cases:
# fails if you pass an invalid option
Keyword.validate([one: "1", two: "2"], [:one])
# => {:error, [:two]}
# fails if you pass a valid option that is not expected that many times
Keyword.validate([one: "1", one: "again"], [:one])
# => {:error, [:two]}
# it can set default values
Keyword.validate([one: "1"], [:one, two: "5"])
# => {:ok, [two: "5", one: "1"]}
# it succeeds if you pass the right options
Keyword.validate([one: "1", two: "2"], [:one, :two])
# => {:ok, [two: "2", one: "1"]}
The delayLongPress
prop on the react native pressable component allows you to specify a delay in milliseconds before the onLongPress
callback prop is executed. Allowing you to control exactly how long a user should press on the element before receiving feedback etc.
If you want to prevent your display from going to sleep while you are doing something that doesn't require input, you can use caffeinate -d
For me this was useful when running a longer script that I wanted to keep an eye on while I was debugging. By running caffeinate -d
in a terminal I was able to prevent the display from sleeping, saving me from pressing a button or moving the mouse all the time.
Today I learned about the unary plus (+
) operator. It attempts to convert its operand into a number, so it's basically a shorthand for Number()
(and uses the same rules around number coercion).
+"7"
// 7
+5
// 5
+true
// 1
+false
// 0
+null
// 0
+undefined
// NaN
+[]
// 0
Today I learned about the -C
flag for git commit
. It's used in Hashrocket's dotmatrix in our git cheddar
alias - git commit --amend -CHEAD
, but I never actually dug into what it does.
From the manual:
-C <commit>, --reuse-message=<commit>
Take an existing commit object, and reuse the log message and the authorship information (including the timestamp) when creating the commit.
Important to note, -C
doesn't open your editor to edit the message, so it's great if you don't need to edit the existing message (if you do want to edit, lowercase -c
will open your editor with the existing message).
-CHEAD
....ohhhh that's why it's called cheddar
🤯
You can set the dimensions of the simulator window to be the exact physical size of the simulated device on the xcode IOS simulator.
Window -> Physical Size or Command + 1
Today I learned a way to view all the enums defined on an ActiveRecord Model.
Suppose I have a class Post
with enums status
and category
:
class Post < ApplicationRecord
enum status: [:draft, :published]
enum category: [:ruby, :rails, :lord_of_the_rings]
end
I can view the enum values for statuses with Post.statuses
and category with Post.categories
, but what if I want to see all the enums on Post
? defined_enums
will do that:
Post.defined_enums
# => {"status"=>{"draft"=>0, "published"=>1}, "category"=>{"ruby"=>0, "rails"=>1, "lord_of_the_rings"=>2}}
You can of course key in to each enum here too:
Post.defined_enums["status"]
# => {"draft"=>0, "published"=>1}
Kamal, the default deployment tool for Rails, has some really great features. One that I just discovered today is kamal secrets
.
You can use this utility to expand sensitive credentials from external sources during deployment. Out of the box, it supports 1Password, Bitwarden (and Secrets Manager), LastPass, AWS Secrets Manager, Doppler, and GCP.
You can run the command for SECRETS
from the .kamal/secrets
file manually to test everything out.
A pre-req for using 1Password is that you will need to install the OP CLI and login to your vault:
brew install 1password-cli
op signin
Next you'll need your account id. You can get that with -
op whoami
Then verify you can read your secrets. The output of the command inside the $(...)
is a stringified JSON -
SECRETS=$(kamal secrets fetch --adapter 1password --account op_account_id --from "op://Example/ApiKeys" KAMAL_REGISTRY_PASSWORD)
The output will look something like this -
\{\"Example/ApiKeys/KAMAL_REGISTRY_PASSWORD\":\"xxxxxxxxxxxxx\"\}
The last part is expanding this. You can pass this JSON string to kamal secrets extract
to extract the value from the key in the JSON.
kamal secrets extract KAMAL_REGISTRY_PASSWORD ${SECRETS}
In MacOS you can go the lock screen quickly using the keyboard by pressing:
CTRL
+ CMD
+ Q
You can change the context of your console session by using the cd
just like you would change the context of your directory.
Here is an example.
# Grab an object, in our example a customer.
c = Customer.all.sample
# Now, 'cd' in to that object.
cd c
# From here you will notice that we are now within
# the context of our object and can call methods on it directly.
pry(<Customer>):1> first_name
=> "Peter"
pry(<Customer>):1> last_name
=> "Parker"
I found this cool tip, as well as some others in this awesome blog post.
It feels like there are an absurd number of options with the built in macOS screenshot tool.
We've seen before you can screenshot the entire screen or select a rectangle and save them as a file or to your clipboard.
Today I Learned there's an option to select an individual window and screenshot the entire window.
You can do this by hitting CMD + shift + 4
(with or without ctrl
), then tap space
. The cursor will change to a camera, and then you can click on the windows you wish to screenshot!
Looks much better than when I try to crop by hand.
I recently got an Enviro hat for my Raspberry Pi Zero W. And in tinkering with it, I found there's a ruby driver for interacting with the BME280 sensor.
You can use it like this -
require "i2c/device/bme280"
require "i2c/driver/i2c-dev"
I2CDevice::Bme280.new(driver: I2CDevice::Driver::I2CDev.new("/dev/i2c-1")).read_raw
In debugging, I learned a few useful commands to check the i2c port. Basically, I just needed to figure out what i2c port to use for the ruby driver.
Check if the i2c port is enabled (raspberry pi os)
sudo raspi-config nonint get_i2c # 0 if enabled, 1 if disabled
List all the i2c interfaces by ls
ing the dev node.
ls /dev/i2c*
Check if the i2c board interface is in the list of kernel modules
lsmod | grep i2c
wc
is well known for giving you word, line and character counts of a file or standard input.
Today I Learned wc
can also tell you the length of the longest line in a file with the -L
flag!
$ cat file.txt
one
two
three
$ wc -L file.txt
5 file.txt
Man pages are full of useful information and trivia. Did you know wc
has been around since Unix Version 1?
Option 1:
Simply typing the terminal command clear
will clear your terminal. This method preserves your current terminal session, meaning you can scroll up to see what was cleared away.
Option 2:
Pressing CTRL
+L
to clear away the terminal. This behaves essentially the same as the clear command, preserving your terminal session.
Option 3:
Pressing CMD
+K
to clear away the terminal. This behaves similarly to the other 2 options, except that it does not preserve your session.
I was curious if I could trigger a rake task from within my Rails application.
After some digging, I found Rails::Command.invoke()
which lets you run rake tasks from inside your Rails app.
Here's an example using db:migrate:
Rails::Command.invoke('db:migrate')
Just pass it the name of your task (db:migrate, your_custom_task, etc) and it will begin processing it.
This is great for automating or triggering rake tasks during bootstrapping or background operations without needing to leave the Rails environment.
I stumbled upon this earlier today, if you're in a terminal and have typed some things in, pressing Esc
, then Backspace
will delete the word on or before your cursor. Ctrl
+ W
will do the same thing.
So if I start typing a word and want to
$ one two three four
^ cursor here
Then Esc
, Backspace
will produce
$ one two three
^ cursor here
It deletes through the beginning of the word, but starts where the cursor is, so you can delete partial if your cursor is in the middle of a word
$ longer command here
^ cursor here
Then Esc
, Backspace
will produce
$ longer and here
^ cursor here
Confirmed both work in recent versions of bash
and zsh
.
To create tables in a markdown file, you can use this syntax:
| Header 1 | Header 2 | Header 3|
| --- | --- | --- |
| Content 1 | Content 2 | Content 3 |
| Content 4 | Content 5 | Content 6 |
And this will output something like this, depending on what you are using to interpret the markdown:
I find this to be useful when displaying side-by-side images in a Github PR
Here is a silly example:
Post Office (B&W) | Post Office (Colorized) |
---|---|
![]() |
![]() |
ActiveSupport has a lot of useful inflections to transform strings, and I often forget which one does what exactly.
#humanize
will capitalize the first word, whereas #titleize
will capitalize every word:
"hi_how_are_you?".humanize
# => "Hi how are you?"
"hi_how_are_you?".titleize
# => "Hi How Are You?"
Actually, it turns out #titleize
is just a special case of #humanize
!
You can automatically run a script on Windows during login by placing files in your users "Programs\Start Up" folder. It's useful for running *.bat
scripts.
You can easily access this location by opening the "Run" dialog - Windows + r
and typing shell:startup
.
If you're an admin, you can also set this for all users by typing shell:common startup
, which will open the shared Start Up folder.
If this is too much, you can also accomplish a similar thing by creating a "Scheduled Task". In the Windows search bar, type "Task Scheduler". Open this then click "Actions" -> "Create Task"
When using the iPhone simulator, under the Window menu bar tab, there is an option for Stay On Top
that will keep the simulator on top of any other windows.
This is very helpful, for example, for keeping the simulator up as you make changes in your text editor.
git rev-list
lists commit objects in reverse chronological order, and as such is an incredibly powerful tool for inspecting your repository.
For example, if you wanted to count all your commits, you can with git rev-list --count HEAD
.
You can also drill down to a specific time range with the --before
and --after
options. To get the count of all commits during 2024, you can run:
git rev-list --count --after="2024-01-01" --before="2025-01-01" HEAD
There are tons of other options you can use to filter this down too, by author, committer, etc.
TypeScript has an Omit utility type that lets you create a new type by excluding specific keys from an existing type. It's perfect for cases where you need a type that's almost the same but without certain properties.
Here's an example:
type User = {
id: number;
name: string;
password: string;
};
type PublicUser = Omit<User, 'password'>;
Now we have a PublicUser
type that includes id and name, but the password field is excluded.
We found it very annoying to navigate to settings>accessibility>display>larger text just to verify that things still look good on phones with larger font sizes.
Turns out you can adjust the font size of the simulator much quicker using the following keyboard shortcuts:
CMD
+Option
++
to increase the font size of the device
CMD
+Option
+-
to decrease the font size of the device
NOTE!
Just keep in mind, even if you have Larger Accessibility Sizes toggled off, you can still get to the larger font sizes this way.
In React Native you can use the onLayout
prop along with the nativeEvent's layout to get the dimensions (in pixels) of a rendered component.
const [viewDimensions, setViewDimensions] = useState({height: 0, width: 0})
return (
<View
onLayout={(e) => setViewDimensions(e.nativeEvent.layout)}
/>
)
Now we have an object containging our view's height and width saved as viewDimensions
Ever been debugging in ActiveRecord and wanted to inverse a query? You can do that without changing the whole query!
User.where(active: true)
# => "select * from users where active = 'true'"
User.where(active: true).invert_where
# => "select * from users where active != 'true'"
It also works with multiple conditions:
User.where(active: true, subscribed: false)
# => "select * from users where active = 'true' and subscribed = 'false'"
User.where(active: true, subscribed: false).invert_where
# => "select * from users where active != 'true' and subscribed != 'false'"
It works on scopes, too:
class User < ActiveRecord::Base
scope :active, -> { where(active: true) }
end
User.active.invert_where
# => "select * from users where active != 'true'"
I recently ran into a situation where I wanted to know the return state of a transaction in ActiveRecord. Basically, I wanted to know if it succeeded or failed, then return true/false
.
There's 3 scenarios to keep in mind to when trying to use this approach:
class TestModel
def save
result =
ActiveRecord::Base.transaction do
SomeModel.last.touch
end
result
end
end
> TestModel.new.save
=> true
ActiveRecord::Rollback
by default, so the transaction block returns nil class TestModel
def save
result =
ActiveRecord::Base.transaction do
raise ActiveRecord::Rollback
end
result
end
end
> TestModel.new.save
=> nil
class TestModel
def save
result =
ActiveRecord::Base.transaction do
raise ActiveRecord::Rollback
end
result
end
end
> TestModel.new.save
StandardError: StandardError from .....
Putting it all together now, we can return a true/false for all scenarios -
class TestModel
def save
result =
ActiveRecord::Base.transaction do
SomeModel.new(attrs).save!
OtherModel.new(other_attrs).save!
end
result
did_save = !!result
did_save
rescue => e
false
end
end
Andrew's TIL from the other day got me thinking about rails generators, and I never knew there was a generator for rake tasks!
There's not a ton of boilerplate in rake tasks, but that's never stopped me from making a typo.
bin/rails g task hello
will generate just the namespace hello
# lib/tasks/hello.rake
namespace :hello do
end
If you have one or more tasks within the namespace, you can include them and the generator will add a task
for each one:
bin/rails g task say hi bye good_day
will generate
# lib/tasks/say.rake
namespace :say do
desc "TODO"
task hi: :environment do
end
desc "TODO"
task bye: :environment do
end
desc "TODO"
task good_day: :environment do
end
end
Rails 8 added a new generator command for creating 1 script files. The docs encourage the use of this folder for one off tasks, data migrations, etc. Previously, you could have used rake tasks to accomplish a similar thing.
Running this generator command will create the script
dir if one does not already exist.
❯ rails g script migration_script
create script/migration_script.rb