Today I Learned

A Hashrocket project

Ready to join Hashrocket? Find Openings here and apply today.

32 posts by marylee @marybethlee11

Rails scopes might just return all resources

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

Rename a local git branch

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>

undef_method vs remove_method

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>

Append items to an array

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!

Postgres regex matching with squiggles

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

Find the position of a substring in postgres

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!).

Set JSON.parse returned object and array classes

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"

There's a "whereami" alias in Pry

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`

Check for members in Ruby

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

Rails has an Array#exclude? method

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!

Swap between STI classes in Rails

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

Remove newlines from strings in Rails

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;"

PostgreSQL query with an array of regexes

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%'])

Reversible integer to string migrations in Rails

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!

How to check Rails migration statuses

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

Postgres locale settings are inherited from OS

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