Raise in rescue blocks without args
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:
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
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
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
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'"
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.
When Postgres is running comparisons, any null values will yield null at the end of the comparison. This is because the null is an unknown value that Postgres can't run the comparison against.
Take the following simple example of a users table and query:
create table users (name text, email text);
insert into users (name, email)
values ('Joe', 'joe@hashrocket.com'),
('Rick', null);
select * from users where email not like '%gmail.com';
-- name | email
-- ------+--------------------
-- Joe | joe@hashrocket.com
-- (1 row)
You'll notice that the Rick
user is not returned in the results.
If you want rows with the null value included in your results, you can coalesce the column to an empty string. This allows Postgres to run the comparison against two known values and return the rows with the null values.
select * from users where coalesce(email, '') not like '%gmail.com';
-- name | email
-- ------+--------------------
-- Joe | joe@hashrocket.com
-- Rick | ΓΈ
-- (2 rows)
Postgres 15 gave us the ability to specify how we want null values to be treated when dealing with unique indexes.
By default, nulls are considered unique values in Postgres:
create table users (name text, email text unique);
-- CREATE TABLE
insert into users values ('Joe', null), ('Jane', null);
-- INSERT 0 2
This default behavior can also be explicitly set using the nulls distinct
clause:
create table users (name text, email text unique nulls distinct);
-- CREATE TABLE
insert into users values ('Joe', null), ('Jane', null);
-- INSERT 0 2
To change the default behavior and prevent nulls from being considered unique values, you can use the nulls not distinct
clause:
create table users (name text, email text unique nulls not distinct);
-- CREATE TABLE
insert into users values ('Joe', null), ('Jane', null);
-- ERROR: duplicate key value violates unique constraint "users_email_key"
-- DETAIL: Key (email)=(null) already exists.
See this change in the Postgres 15 release notes
Say you have an Activerecord object that you are making changes to.
user = User.last
user.update(first_name: "Joe", last_name: "Hashrocket")
With ActiveModel's Dirty
module, you can view the changes made to the model even after it has been saved using the saved_changes
method
user.saved_changes
# {"first_name"=>[nil, "Joe"], "last_name"=>[nil, "Hashrocket"]]}
PSQL has some helpful commands built in for viewing foreign data servers and tables if you are using them:
\des
- list foreign servers
\deu
- list user mappings
\det
- list foreign tables
\dtE
- list both local and foreign tables
\d <name of foreign table>
- show columns, data types, and other table metadata
Bundler allows you to pass group options when running commands in the terminal:
bundle outdated --group test
bundle upgrade --group test
If you ever need to find the location of your Postgres config file, you can connect to Postgres and run:
show config_file
This command will return the absolute path for the config file.
Given you have an array of objects that you may want to split apart based on a value on one of the objects, you can use slice_after
(there's also slice_before
, which behaves the same way).
array = [
{activity: "traveling", ticket: "123"},
{activity: "working", ticket: "123"},
{activity: "awaiting_assignment", ticket: ""},
{activity: "traveling", ticket: "234"},
{activity: "refueling", ticket: "234"},
{activity: "traveling", ticket: "234"},
{activity: "working", ticket: "234"},
{activity: "awaiting_assignment", ticket: ""}
]
array.slice_after { |i| i.activity == "awaiting_assignment" }
# Returns:
[
[
{activity: "traveling", ticket: "123"},
{activity: "working", ticket: "123"},
{activity: "awaiting_assignment", ticket: ""}
],
[
{activity: "traveling", ticket: "234"},
{activity: "refueling", ticket: "234"},
{activity: "traveling", ticket: "234"},
{activity: "working", ticket: "234"},
{activity: "awaiting_assignment", ticket: ""}
]
]
As Ruby developers, we're often looking for ways to reduce time consuming lookups in our code. A lot of times, that leads us to memoizing those lookups with the common ||=
operator.
However, if our lookups return a nil or falsey value, our memo will actually keep executing the lookup:
def ticket
@ticket ||= Ticket.find_by(owner:)
end
This code essentially boils down to:
def ticket
@ticket = @ticket || Ticket.find_by(owner:)
end
If our find_by
in the example above returns nil, the code will continue to run the find_by
every time we call the ticket
method.
To avoid this, we can shift our pattern a bit, and look to see if we have already set our instance variable or not:
def ticket
return @ticket if defined?(@ticket)
@ticket = Ticket.find_by(owner:)
end
You can lookup your postgres function OIDs (object identifiers) using the pg_proc
table:
select oid from pg_proc where proname ='my_function_name';
This is very useful if you need to find more info about a function but only know the function name.
Passing null
or all
to the limit clause effectively ignores the limit clause.
-- both statements return all user emails
select email from users limit null;
select email from users limit all;
Ruby has a method to remove repeating characters in a string called squeeze
"foobar".squeeze
# => "fobar"
"foo foo bar".squeeze
# => "fo fo bar"
It also accepts args to narrow down the specific characters for which you would want to remove repeats:
"foobar hello world".squeeze("o")
# => "fobar hello world"
"foobar hello world".squeeze(" ")
# => "foobar hello world"
ActiveRecord's scopes are meant to be composable and intended to only ever return an ActiveRecord relation.
If you make a mistake with your scope and have it return something like a nil
or false
, Rails will return all records for that class in order to maintain composability.
If you are intentionally writing something that might return an empty value, use a class method rather than adding a scope in order to prevent bugs
Ever wanted to rename a local git branch? The git branch -m
command is your friend
Want to rename a branch that's not currently checked out?
git branch -m <original_name> <new_name>
Or to rename your current local branch, you can exclude the original name in the git branch args:
git branch -m <new_name>
To review the git history of a specific file, you can use the git log
command and pass the relative path for the file in question:
git log app/models/user.rb
You can also pass the -p
flag to auto expand the diffs for the file:
git log -p app/models/user.rb
The cp
command has a -n
flag that you can use to prevent the copied file from overriding another file if one with the same name exists
When a naming collision occurs and the -n
flag is present, the copy command does nothing
Need to do exponential calculations in Ruby? Use the **
operator:
5 ** 2
# => 25
2 ** 3
# => 8
4 ** 4
# => 256
Ruby's undef_method
and remove_method
are both methods for removing a method from a class, but there are subtle differences between the two.
Say we have two classes that both define the method name
, with one class inheriting from the other:
class Human
def name
"homo sapien"
end
end
class Child < Human
def name
"small homo sapien"
end
end
remove_method
fully deletes a method from a particular class, but will still look for the method on parent classes or modules when called on the particular class:
child = Child.new
child.name
# => "small homo sapien"
class Child
remove_method :name
end
child.name
# => "homo sapien"
undef_method
in contrast will prevent Ruby from looking up the method on parent classes
child = Child.new
child.name
# => "small homo sapien"
class Child
undef_method :name
end
child.name
# => raises NoMethodError
# undefined method `name' for #<Child:0x00007ffd91a007a8>
Ever been working with a string full of new lines?
Ruby has a lines
method for helping with those strings:
"wibble\nwubble".lines
# => ["wibble\n", "wubble"]
"wibble\nwubble".lines(chomp: true)
# => ["wibble", "wubble"]
Today I came across yet another way to add items to an array in ruby
I already knew about push
and <<
, but did you know that there's also an append
?
[1,2,3].push(4)
# => [1,2,3,4]
[1,2,3] << 4
# => [1,2,3,4]
[1,2,3].append(4)
# => [1,2,3,4]
append
is ultimately just an alias for push
, but always good to know!
By default, the Rails fixture_file_upload
helper looks in the spec/fixtures/files
directory.
You can change this default path in your spec helper:
RSpec.configure do |config|
config.file_fixture_path = "spec/fixtures/not_files"
end
Postgres supports POSIX regex pattern matching using the squiggle (~
) operator
-- Check for a match
select 'wibble' ~ 'ubb';
-- returns false
select 'wibble' ~ 'ibb';
-- returns true
-- Case insensitive match checking
select 'wibble' ~ 'IBB';
-- returns false
select 'wibble' ~* 'IBB';
-- returns true
-- Check for no match
select 'wibble' !~ 'ibb';
-- returns false
select 'wibble' !~ 'ubb';
-- returns true
Full postgres pattern matching documentation can be found here
You can use carets in bash to quickly do string replacements in your previous commands:
$ echo "wibble wubble"
wibble wubble
$ ^bb^gg
echo "wiggle wuggle"
wiggle wuggle
Here's the documentation
h/t Dillon
Postgres has a strpos
function for finding the position of a substring in a given string:
select strpos('wibble', 'ibb');
The function returns an integer representing the location of the substring in the provided string, or a 0
if the substring cannot be found in the provided string (the locations are 1-based indexes, so you don't have to worry about collisions!).
My normal use of the exists?
method in ActiveRecord has always been to toss it on the end of a query of some kind:
Animal.where(name: "dragon").exists?
# => false
However, I discovered that the method can be called directly on a class and passed options:
Animal.exists?(name: "dragon")
Animal.exists?(123)
Animal.exists?(id: [123, 345])
Check out the full documentation for more examples
As of git version 2.23, a new command was introduced for switching branches -- git switch
Want to switch to an existing branch?
git switch <branch_name>
Want to switch to a new branch?
git switch -c <new_branch_name>
Full documentation: https://git-scm.com/docs/git-switch
h/t Dillon
In Rails, you can look up your current database name using the ActiveRecord connection method current_database
:
ActiveRecord::Base.connection.current_database
# => "hashrocket_development"
By default, the Ruby JSON.parse
method returns a ruby Hash for any json object, and a ruby Array for any json array.
However, you can customize the returned object classes using the object_class
and array_class
options:
source = JSON.dump({ wibble: "wubble", data: [1,2,3] })
result = JSON.parse(
source,
object_class: OpenStruct,
array_class: Set
)
# => #<OpenStruct wibble="wubble", data=#<Set: {1, 2, 3}>>
result.data # => #<Set: {1, 2, 3}>
result.wibble # => "wubble"
Since version 0.10.0, the pry
gem has shipped with a built in alias for whereami
: the extremely convenient @
Other useful aliases can be found using the help
command:
[1] pry(main)> help
Aliases
!!! Alias for `exit-program`
!!@ Alias for `exit-all`
$ Alias for `show-source`
? Alias for `show-doc`
@ Alias for `whereami`
clipit Alias for `gist --clip`
file-mode Alias for `shell-mode`
history Alias for `hist`
quit Alias for `exit`
quit-program Alias for `exit-program`
reload-method Alias for `reload-code`
show-method Alias for `show-source`
Git allows you to stash untracked files with the --include-untracked
or -u
option:
git stash -u
Git also has an --all
or -a
option for stash that will stash both untracked and ignored files:
git stash -a
Ruby's Enumerable
class has a member?
method that returns a boolean.
For arrays, the method checks if the supplied value is included (similar to ['a'].include?('a')
):
[:a, :b].member?(:b) # => true
[:a, :b].member?(:c) # => false
For hashes, the method checks if the supplied value is included in the keys for the hash:
{ a: 'b' }.member?(:a) # => true
{ a: 'b' }.member?(:c) # => false
In my vendetta against the unless
expression in ruby, I came across a use case where I wanted to execute code only if an item was missing from an array.
I could easily do:
unless ['a', 'b', 'c'].include?('d')
# do a thing
end
But I wanted to replace the unless
with an if
, which led me to wonder if there was an exclude?
method for arrays in ruby.
Turns out, ActiveSupport
extended the Enumerable
class to introduce exactly what I was looking for!
if ['a', 'b', 'c'].exclude?('d')
# do a thing
end
Hooray!
Rails allows you to swap between single table inheritance classes with the becomes
method. This method creates a new instance of the desired class, and passes the attributes from the original record to the new instance.
So if I have two classes, User
and Admin
, where Admin
inherits from User
:
class User
# ...
end
class Admin < User
# ...
end
I can change between the two classes using becomes
:
user = User.first # returns an instance of the User class
user.class # => User
user.id # => 1
user.name # => Joe Hashrocket
admin = user.becomes(Admin) # returns an instance of the Admin class
admin.class # => Admin
admin.id # => 1
admin.name # => Joe Hashrocket
Rails has a method called squish
that will remove newlines from strings. It works very nicely with heredocs, where you may want readability but don't really want the newlines.
Without squish
:
<<~SQL
update posts
set status = 'public'
where status is null;
SQL
# "update posts\nset status = 'public'\nwhere status is null;\n"
With squish
:
<<~SQL.squish
update posts
set status = 'public'
where status is null;
SQL
# "update posts set status = 'public' where status is null;"
You can quickly toggle a "NOT NULL" constraint in a Rails migration with the change_column_null
method.
To add the constraint:
change_column_null :users, :email, false
To remove the constraint:
change_column_null :users, :email, true
I recently wanted to query my Postgres database by matching a column based on an array of regular expressions:
To query where the column matches all expressions in the array:
select *
from my_table
where my_column ilike all (array['%some%', '%words%'])
To query where the column matches at least one, but not necessarily all, of the expressions in the array:
select *
from my_table
where my_column ilike any (array['%some%', '%words%'])
You can easily remove the first elements from an array in ruby without mutating the array by using drop
array = [ 1, 2, 3 ]
array.drop(1) # => [ 2, 3 ]
array # => [ 1, 2, 3 ]
array.drop(2) # => [ 3 ]
array # => [ 1, 2, 3 ]
You can now exit the postgres console by typing "exit" or "quit"
$ psql
psql (11.0)
Type "help" for help.
postgres=# exit
$ echo wow
wow
You can specify optional arguments in a define_method
call in ruby:
define_method :my_method do |options = {}|
# do stuff
end
I recently needed to convert an integer column in Rails to a string, and wanted to make sure that the migration would be reversible. I specified the up
and down
methods, but found that I couldn't reverse the migration because the column type couldn't be automatically cast back into an integer.
As it turns out, Rails allows us to specify how to cast the column with the using
option:
def up
change_column :users, :zip, :string
end
def down
change_column :users, :zip, :integer,
using: "zip::integer"
end
This builds the sql:
ALTER TABLE users
ALTER COLUMN zip
TYPE integer
USING zip::integer
Good to go!
You can connect to a different database in the psql
console using the \connect
command (or \c
for short)
$ psql
psql (10.5)
Type "help" for help.
postgres=# \c example
You are now connected to database "example" as user "root".
example=#
You can check the status of your migrations in Rails by running rails db:migrate:status
This command returns your database name, as well as a list of all your migrations, with their name and status
database: my-database-dev
Status | Migration ID | Migration Name
--------------------------------------
up | 201803131234 | Create users
up | 201803201234 | Create blogs
down | 201804031234 | Create posts
Rails 5 added an internal metadata table that saves the rails environment to the database.
This metadata table can lead to an ActiveRecord::EnvironmentMismatchError
when restoring a staging/production database to the development environment.
To fix the error, you can run rails db:environment:set
By default, postgres inherits locale settings from operating system, which can greatly affect sort. Comparing linux and unix, both using the locale en_US.UTF-8
, we see the following sort outputs:
Unix
select name from unnest(array['No one', 'None', ' Not']) name order by name;
name
--------
Not
No one
None
Linux
select name from unnest(array['No one', 'None', ' Not']) name order by name;
name
--------
None
No one
Not
You'll notice that on the linux system, whitespaces are ignored during sorting.
To get consistent behavior across operating systems, you can use the postgres provided C
locale:
select name from unnest(array['No one', 'None', ' Not']) name order by name collate 'C';
name
--------
Not
No one
None