Use Physical Device Size on IOS Sim window
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
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
A change to Ruby 3.4 will print a warning while using the verbose flag -w
when a method is passed a block and the block is not used by that method.
Here's our example file scratch.rb
-
def print_message(message) = puts message
print_message("hello_world") { puts "hello anonymous" }
> ruby scratch.rb
hello world
> ruby -w scratch.rb
scratch.rb:3: warning: the block passed to 'Object#print_message' defined at scratch.rb:1 may be ignored
hello world
https://www.ruby-lang.org/en/news/2024/12/25/ruby-3-4-0-released/
When you generate a new Rails app, it comes with the web-console
gem, a handy helper for embedding a rails console in the browser.
This can be useful in development
for debugging things in the same process as your webserver. In my case, I used it to test out some action cable related things.
In your controller, just add console
to the action -
class AppointmentsController < ApplicationController
def index
console
end
end
In Rails, if you want to ensure that you are using the same database connection from the connection pool to execute a number of commands, you can use the ActiveRecord::Base.with_connection
method.
This method yields a single connection from the pool for you to execute your commands against:
ActiveRecord::Base.with_connection do |conn|
conn.execute("select * from sessions")
end
It is important to note that the connection yielded is taken out of the pool until the processing in the block is complete.
You can use Kernel.struct/2
to convert a keyword list (or Enumerable) into a struct.
I found this useful when working with a new social media integration. I read some key-values out of the config environment and needed to convert these into a named struct.
> creds = Application.get_env(:tilex, __MODULE__)
[username: "foo", password: "bar"]
> struct(BlueskyEx.Client.Credentials, creds)
%BlueskyEx.Client.Credentials{
username: "foo",
password: "bar"
}
When you pass keys that are not part of the struct, they are discarded for you. If you want to raise an error, you can use the related struct!/2
function
In React Native, the RefreshControl
component can be placed inside of a scrollview component to handle refreshes. When the user scrolls up, the onRefresh
function will be called where you can handle your refresh/query logic.
You can keep your xcode IOS simulator window on top of all your other windows by selecting Window -> Stay on top
Registers in (n)vim contain a lot of super useful data. To wit, the %
register stores the path of the current buffer. Some useful things we can do with this register:
Insert Mode
: Ctrl + R
, %
Normal Mode
: "%p
"let @+=@%
(copy the %
buffer into the +
buffer, which is usually used by the system clipboard), or echo it Today I learned that ANSI Escape Codes are a thing.
I was writing some Rails migrations and wanted nice readable output and a quick way to tell how things were going. So, I wanted some colored text to be displayed as output. This was how I learned about ANSI Escape Codes.
In short, you want to escape your string using \e
then give it the prefix [
and then give it a series of codes to format your text.
For example, you can color all further text green using the code 32m
or red using 31m
.
If you want text to go back to normal give it the code 0m
.
Here is an example:
You can do other formatting than just color, but I found these to be helpful for what I needed and I didn't realize how simple it was.
Today I learned you can nest FactoryBot traits within other traits. Traitception?!
Say you have a blog post model with a deleted_at
attribute, and an optional deleted_by
attribute. You could have:
FactoryBot.define do
factory :post do
trait :deleted do
deleted_at { Time.current }
end
trait :deleted_by_admin do
deleted
deleted_by { :admin }
end
end
end
There the deleted
in deleted_by_admin
references the deleted
trait above it.
You could alternatively define a new factory that composes the two traits, but it's always nice to have options.
factory :admin_deleted_post, traits: [:deleted, :deleted_by_admin]
In a Rails task if you need to load in your rails application (to get access to your models, etc.), you have to call :environment
in the task:
task task_name: :environment do
...
end
I kind of always took this for granted, and never thought much about it. Then, in Xavier Noria's 2024 Rails World talk he mentioned that :environment
itself is a task - and that syntax is actually saying your task depends upon :environment
and run that task before your task runs. 🤯
So I decided to look up what the environment task actually does:
task :environment do
ActiveSupport.on_load(:before_initialize) { config.eager_load = config.rake_eager_load }
require_environment!
end
require_environment!
... requires your environment, specifically it requires your config/environment.rb
which runs Rails.application.initialize!
- which is what actually starts your rails app.
Cool!
I use the rspec change matcher a lot to check the before and after values of something while the subject under test executes. A convoluted example:
RSpec.describe "expect change from to" do
it do
x = 1
expect{ x = x + 1 }.to change { x }.from(1).to(2)
end
end
And sometimes I use to it to verify something doesn't change.
RSpec.describe "expect not to change" do
it do
x = 1
expect{ nil }.not_to change { x }
end
end
Which is great - but sometimes I want to make sure my understanding of the initial state is correct and want to verify it didn't change from its initial value - in this case 1.
RSpec.describe "expect not to change from" do
it do
x = 1
expect{ nil }.not_to change { x }.from(1)
end
end
And if that from
value is wrong, I'll get a nice message explaining what's wrong.
RSpec.describe "expect not to change from" do
it do
x = 1
expect{ nil }.not_to change { x }.from(0)
end
end
# => expected `x` to have initially been 0, but was 1
You can use comparison operators like <
, <=
, >
, and >=
(and more!) in rspec tests:
RSpec.describe "Comparison Operators" do
it do
expect(5).to be < 7
expect(5).to be >= 5
end
end
I ran into a problem where I wanted to use a case
statement to match against a regex, but also capture some values from the matched data.
Say I want to match on a pair of numbers wrapped in parens like (2,4)
and add them together.
If I match without capture groups I have to grab the numbers again in the when
block:
x = "(2,4)"
case x
when /\(\d+,\d+\)/
a,b = x.scan(/\d+/)
a.to_i + b.to_i
end
# => 6
Seems a shame to have perform another op to get a
and b
. What if we used capturing groups like /\((\d+),\(d+)\)/
? How do we refer to those captured values inside the when
? We can use some of ruby's special variables, namely $1
and $2
to refer to the captured groups of the last regexp.
x = "(2,4)"
case x
when /\((\d+),(\d+)\)/
$1.to_i + $2.to_i
end
# => 6
$1
and $2
feel a little magic, so we can name our capture groups group1
and group2
, and access those from $~
(the MatchData
of the last regexp).
x = "(2,4)"
case x
when /\((?<group1>\d+),(?<group2>\d+)\)/
$~[:group1].to_i + $~[:group2].to_i
end
# => 6
The Enum Module in Elixir has the frequencies/1
function, which is useful for counting the occurrences of each element in a list.
> Enum.frequencies(["dog", "cat", "dog", "bird", "dog", "cat"])
%{"bird" => 1, "cat" => 2, "dog" => 3}
There's also frequencies_by/2
, where the 2nd argument is a function that normalizes the element/key before counting.
> Enum.frequencies_by(["a", "b", "a", "C", "A"], fn key ->
String.upcase(key)
end)
%{"A" => 3, "B" => 1, "C" => 1}
Let's say you want to check if a number is between 1 and 3 in ruby.
You can use #include?
:
(1..3).include?(2)
# => true
(1..3).include?(3)
# => true
(1..3).include?(5)
# => false
You can also use the threequals operator ===
:
(1..3) === 2
# => true
(1..3) === 3
# => true
(1..3) === 5
# => false
Both methods can be used on inclusive and exclusive ranges
If you want to see the device keyboard while using the Xcode simulator, use ⌘ + K
In Tailwind you can use certain classes to wrap text within an element.
You could use something like text-wrap
or whitespace-pre-wrap
, or any of the other classes.
However, if a single word is too long it will continue on past the element it is contained in.
Kind of like this:
<div class="text-wrap">
<!-- really long text -->
</div>
HEREisAreallyLONGwordHEREisAreallyLONGwordHEREisAreallyLONGwordHEREisAreallyLONGwordHEREisAreallyLONGwordHEREisAreallyLONGwordHEREisAreallyLONGwordHEREisAreallyLONGwordHEREisAreallyLONGword
Well, just add the Tailwind class break-words
and it will break up a word when it reaches the end of the element, and you'll get something more like this:
<div class="text-wrap break-words">
<!-- really long text -->
</div>
HEREisAreallyLONGwordHEREisAreallyLONGwordHEREisAreallyLONGwordHEREisAreallyLONGwordHEREisAreallyLONGword HEREisAreallyLONGwordHEREisAreallyLONGwordHEREisAreallyLONGwordHEREisAreallyLONGword
You can use the rails model generator to generate namespaced models. I always have a hard time remembering the syntax for namespaces, but it's pretty straightforward.
Say I want to create a model Blog::Post
. The generator for this is:
rails generate model blog/post title:string ...
This will generate the following files (assuming you're using rspec and FactoryBot)
invoke active_record
create db/migrate/20241028193321_create_blog_posts.rb
create app/models/blog/post.rb
create app/models/blog.rb
invoke rspec
create spec/models/blog/post_spec.rb
invoke factory_bot
create spec/factories/blog/posts.rb
If the namespace already exists, you'll be prompted to either overwrite or keep the existing app/models/blog.rb
file (you'll probably want to keep the existing one).
Happy generating!
I sometimes press the r
key on accident when in netrw. This toggles the sort direction of the files and folders. You can hit it one more time to change it back to default.
You can also try sorting by different parameters with the s
key - name, file type, or file size.
With Expo's CLI, the command npx expo run:ios
will build and run your application in xcode's IOS simulator. You will need to have Xcode installed in order for the command to work. If you receive an error stating Xcode must be fully installed before you can continue
, you may need to navigate to Xcode's settings and set a version of command line tools under the locations
tab.
If you want to see the file sizes for Folders and not just files, press CMD+J
in Finder and then make sure Calculate all sizes
is checked and then hit Use as defaults
to save this as the default setting.
Using the Ruby on Rails migration generator is great to get up and running but always seems to require just a little bit more effort once the migration file is created.
There is a shorthand syntax for the cli interface of the generator but I can never remember it past a column name and type... so I'm putting this here to remember it for all of us!
To create an index just continue adding a bit more information to your column declaration. Let's start with this as a base so we're on the same page:
rails generate add_name_to_widgets name
This will create this migration:
class AddNameToWidgets < ActiveRecord::Migration[7.0]
def change
add_column :widgets, :name, :string
end
end
Now by making a minor adjustment to the original cli call we can generate the index too:
rails generate add_name_to_widgets name:string:index
class AddNameToWidgets < ActiveRecord::Migration[7.0]
def change
add_column :widgets, :name, :string
add_index :widgets, :name
end
end
And you know what? We can do unique indexes as well:
rails generate add_name_to_widgets name:string:uniq
class AddNameToWidgets < ActiveRecord::Migration[7.0]
def change
add_column :widgets, :name, :string
add_index :widgets, :name, unique: true
end
end
I'm sure many of us are familiar with the Ruby on Rails migration generator...
rails generate migration add_name_to_widgets name
which generates a migration that looks like so:
class AddNameToWidgets < ActiveRecord::Migration[7.0]
def change
add_column :widgets, :name, :string
end
end
Then to make the name
column have a non-null constraint an addition of null: false
would be needed:
class AddNameToWidgets < ActiveRecord::Migration[7.0]
def change
add_column :widgets, :name, :string, null: false
end
end
However, now in Rails 8 an adjustment was made to allow this to be done from the generator shorthand
rails generate migration add_name_to_widgets name:string!
By adding the exclamation point after the column type a migration will now be generated as we wanted before but without the additional manual edit:
class AddNameToWidgets < ActiveRecord::Migration[8.0]
def change
add_column :widgets, :name, :string, null: false
end
end
h/t Akshay Khot
The postgresql package for Ubuntu includes some useful commands for managing your database cluster. I learned about these today when we upgraded postgres versions on a digital ocean droplet.
The ones that I learned of were -
pg_lsclusters
for listing the available clusters on the machine and their status pg_upgradecluster OLD_VERSION CLUSTER_NAME
for upgrading to a new cluster. If you don't specify the optional -v
(new version), it's assumed you want the most recent version you have installed. This handles copying your old conf files to the new version and setting it up on the originally specified port. pg_dropcluster VERSION CLUSTER_NAME
for removing a cluster. Optionally you can specify --stop
to force a shutdown and delete. pg_createcluster VERSION CLUSTER_NAME
which will create a new cluster given the version and name param. The default is to not start this new cluster on creation, but you can optionally specify the --start
flag to immediately start the cluster. https://manpages.ubuntu.com/manpages/trusty/man8/pg_createcluster.8.html https://manpages.ubuntu.com/manpages/trusty/en/man8/pg_dropcluster.8.html https://manpages.ubuntu.com/manpages/trusty/man8/pg_upgradecluster.8.html https://manpages.ubuntu.com/manpages/xenial/en/man1/pg_lsclusters.1.html
File.stat/2
will return data from the path passed to it, including the accessed time atime
and the modified time mtime
.
I'm very used to ruby's gsub(pattern, replacement)
that does a [g]lobal [sub]stitution on a string - that is, it replaces all occurrences of the pattern in the string. If you wanted to just replace the first occurrence of the pattern you could use sub
, but I so rarely need to do that I forgot it existed.
Javascript's equivalent, replace
, handles things a little bit differently. So much so I was surprised by it's behavior.
replace(pattern, replacement)
pattern
can be a string
or a RegExp
, but you get different replacement behavior depending on which you use.
pattern
is a string, only the first occurrence of pattern
will be replaced. pattern
is a RegExp
, by default it will replace the first occurrence, unless you pass the global flag to the pattern. So if I wanted to replace asdf
in with hjkl
in a string, replace(/asdf/, "hjkl")
will replace just the first occurrence. To replace all occurrences, it needs to be replace(/adsf/g, "hjkl)
(note the global g
flag in the regex). So maybe the moral of the story is to always use a regex (and remember your flags!).
In Elixir, there's strings "hi"
and charlists 'hi'
.
You can also use the ~c sigil to denote a charlist -
~c"hi there"
There's also a word list sigil ~w
which behaves similarly to %w
in Ruby. It constructs a list of words -
~w(blue red green)
=> ["blue", "red", "green"]
Also worth mentioning, the ~r
sigil for regexes -
"foo" =~ ~r"hashrocket"
=> false
"hashrocket" =~ ~r/hashrocket/
=> true
There's many other sigils in Phoenix and Elixir. Be sure to check out the docs!
h/t to Jack Rosa