Today I Learned

hashrocket A Hashrocket project

Create a Date object for a specific day

Say you have some date-specific functionality, and you want to test for a specific day of the week.

Date#commercial is what you're looking for! It will create a Date object for you based on the year,week, and day that you give it.

require 'date'

# Wednesday (3) of week 1 of year 2023
Date.commercial(2023, 1, 3)

In Rails we can take this a step further, for example, to get Friday of this week:

Date.commercial(Date.current.year, Date.current.cweek, 5)

In your testing you can simply make use of the Rails TimeHelpers to travel to that specific date you need:

next_friday = Date.commercial(Date.current.year, Date.current.cweek + 1, 5)

travel_to next_friday do
  # Friday specific code
end

Arbitrary Values with Tailwind

You can set custom values in tailwind, without customizing your theme like this:

<div class="w-[50rem]">...</div>

This bracket syntax works with basically anything in tailwind. For example, here's font size.

<p class="text-[14px]">...</p>

This is super helpful at times when you don't want to bother with tailwind's size classes.

Custom Flash Messages in Rails

Why Aren't My Flash Messages Working?

Turns out, there's 2 keys that Rails supports by default for flash messages. Those are alert and notice; you can use them like this in your controller:

redirect_to users_path, notice: "User created successfully"
# - or -
render :new, alert: "An error prevented the user from being saved"

But if your flash rendering code is generic enough, you might notice that explicitly setting a key/message on flash will work for values other than the defaults:

flash[:success] = "User created successfully"
redirect_to users_path

TIL Rails has a helper that will allow us to add our own custom flash messages - add_flash_type. You can use this an a controller (re: ApplicationController) to enable new flash message types. This will allow you to do the one liners in render and redirect calls:

class ApplicationController < ActionController::Base
  add_flash_type :my_flash_type
end

# ...

redirect_to users_path, my_flash_type: "User created successfully"
# - or -
render :new, my_flash_type: "An error prevented the user from being saved"

In addition, it will also add a variable in our views to retrieve this message:

<%= my_flash_type %>

https://api.rubyonrails.org/classes/ActionController/Flash/ClassMethods.html

Convert Changeset Into Schema With `apply_changes`

Often times, it's necessary to perform some intermediate operations on a Ecto changeset before saving it to the database. It can be easier to deal with the underlying schema. And you might need the entire schema rather than only the changes in the current changeset.

You can use the apply_changes/1 function to apply that changeset and receive schema with which you can perform operations. It's worth noting that the data is not validated when changes are applied, so care needs to be taken to ensure validity before an attempt to save that record.

iex> lead_schema = Lead.changeset(%{name: "Andrew", email: "Andrew.Vogel@hashrocket.com"})
                   |> apply_changes() 
%Lead{name: "Andrew", email: "Andrew.Vogel@hashrocket.com"}

iex> lead_schema |> downcase_email() |> do_some_other_processing()

https://hexdocs.pm/ecto/Ecto.Changeset.html#apply_changes/1

Pretty-print JSON in Ruby

When receiving a JSON payload it can, of course, be useful to see it in a more readable way. Turns out there is a built in utility in Ruby that can help with this.

Kernal#j & Kernal#jj

Utilizing the Kernal#j method:

foo = {name: "Matt", company: "Hashrocket"}

=> j foo
{"name": "Matt", "company": "Hashrocket"}

Utilizing the Kernal#jj method:

foo = {name: "Matt", company: "Hashrocket"}

=> jj foo
{
  "name": "Matt",
  "company": "Hashrocket"
}

Phoenix LiveView slot attributes

Today I learned how to define an attribute on a Phoenix LiveView slot using a do block:

slot :column
  attr :field, :atom
  attr :sortable, :boolean, default: false
end

def table(assigns) do
  ~H"""
  <table>
    <tr>
      <th :for={col <- @column}>
        <%= col.label %>
        <.sort_by :if={col.sortable} field={col.field}/>
      </th>
    </tr>
  </table>
  """
end

This way we can use our slots with attributes:

def render(assigns) do
  ~H"""
    <.table>
      <:colum field={:name} sortable>Name</:colum>
      <:colum field={:price}>Price</:colum>
      <:colum>Actions</:colum>
      ...
    </.table>
  """
end

Check this out

Curl `-T/--upload-file` in Faraday

I had a bit of trouble trying to find docs on how to do a curl --upload-file request with ruby. This flag is a special flag that tells curl to generate a PUT request with the body being the file(s) to upload to the remote server.

In my case, I wanted to upload a single file, and I accomplished this with the faraday and faraday-multipart gem:

require 'faraday'
require 'faraday-multipart'

conn = Faraday.new("https://example.com") do |f|
  f.request :multipart
end

upload_file = File.open("./path/to/image.jpg")

conn.put("/file-upload") do |req|
  req['Content-Type'] = 'image/jpeg'
  req['Content-Length'] = upload_file.size.to_s
  req.body = Faraday::Multipart::FilePart.new(
    upload_file,
    'image/jpeg'
  )
end

Part of the magic here is that you need to explicitly set the Content-Type and the Content-Length header.

https://github.com/lostisland/faraday-multipart

Set an input's cursor position with Javascript

If you need to hijack an input's cursor position, you can use .setSelectionRange(). This function takes two zero-based index arguments: a starting position and end position and makes makes a text-selection based on those start and end points. With this understanding, we can pass in the same index for both the start and end point to manually move the cursors position without making a selection. For example:

// move the cursor to the end of an input
inputElement.setSelectionRange(inputElement.value.length, inputElement.value.length)

//or move the cursor to a specific index
inputElement.setSelectionRange(3, 3)

//or make a selection with a different start/end
inputElement.setSelectionRange(3, 6)

Disable strong parameters in rails

⚠️ Don't do thisYou should never do this. This was used for a non-public internal active admin application that needed to be updated. This app was not intended for public use. Please don't do this.
# config/environments/development.rb
require "active_support/core_ext/integer/time"

Rails.application.configure do
  config.action_controller.permit_all_parameters = true
end

Setting the url_options hash in the controller

When grabbing the url of an ActiveStorage record through any of these methods: ActiveStorage::Blob#url,ActiveStorage::Variant#url,ActiveStorage::Preview#url you may find yourself running into this error:

Cannot generate URL for <FILENAME> using Disk service, 
please set ActiveStorage::Current.url_options.

This can be resolved in the controller by utilizing a concern that ActiveStorage provides for us; ActiveStorage::SetCurrent.

It would look like this in your controller:

class MyController < ApplicationController
  include ActiveStorage::SetCurrent
  
  # The rest of your controller...
end

And now whenever you go to call #url on your ActiveStorage record, it will know where to generate the url from!


Including ActiveStorage::SetCurrent sets the ActiveStorage::Current.url_options hash to match the current request.

Control printable backgrounds in CSS

Have you created an amazing print version of your html page and when you go to print it out, it's not looking quite the same?

Hey those background colors are all missing!

Turns out that your user-agent (browser) is given control on how your elements should appear in different scenarios, like printing.

This is why when you print out a page with a background image, said image is generally not printed and you get a clean print.

Here the stamp style looks super elegant with our cornflowerblue background but when printing that we lose the look of the stamp due to the background being missing.

.stamp {
  background-color: cornflowerblue;
  border-radius: 5px;
  padding: 10px;
}

There is a way to control this though. The print-color-adjust css directive can be set so that your styles will do your printable bidding.

By default print-color-adjust is set to economy. I like to think of this as if we're saving money in unused ink.

With a small change to:

.stamp {
  background-color: cornflowerblue;
  border-radius: 5px;
  padding: 10px;
  print-color-adjust: exact;
}

We now get our stamp style that looks great in our browser to look great on a printed page as well.

!important classes in tailwind

If you need to add !important to one of your Tailwind classes, you simply need to add ! before the class name.

Here is a crude example:

Blue Background 🔵

<div class="bg-red-500 bg-blue-500">
  <!-- Insert content here -->
</div>

Red Background 🔴

<div class="!bg-red-500 bg-blue-500">
  <!-- Insert content here -->
</div>

When using this with responsive breakpoints, place it after the variant modifier.

Valid 😁

<div class="md:!bg-red-500">
  <!-- Insert conent here -->
</div>

Invalid 😢

<div class="!md:bg-red-500">
  <!-- Insert content here -->
</div>

Pattern Matching Args with Exact Values

In elixir there is a handy trick for pattern matching for exact values. Let's say we have a function head_match that takes two arguments, a string and a list, that checks if the string argument is the same as the head of the list. We could use pattern matching like this:

def head_match(head, [head, _tail]) do
  IO.puts("Thats the head of the List!")
end
#this will match if we called it like head_match("first_word", ["first_word", "second_word"])

See how we named the first argument head and used that same name as the list head? This means that the function will only match if those two values are the exact same rather than just matching the structure of the arguments.

Shorthand Elixir Anonymous Functions

Elixir has a really cool syntax for writing anonymous functions (unnamed functions). It goes like this:

# The '&' operator is used to define the function and its arguments
putter = &(IO.puts &1)
putter.("Today I learned")
#...> Today I learned
#...> :ok

&1 refers to the functions first argument; in this example, we used "Today I learned". You can use more arguments with &2, &3, etc... For example:

combiner = &(&1 <> &2)
combiner.("Foo", "Bar")
#...> "FooBar"

Also notice that we are not calling either of these functions like function(arg1, arg2), instead we are calling them like function.(arg1, arg2). We have to use . because we are not actually naming these functions, we are only assigning them a reference, hence "anonymous" functions.

Rails' .insert_all method is too naive

Rails requires a unique index in order to use the .insert_all methods. This requirement can make this method very brittle and unusable. If your conflict target is the table's primary key, this won't work unless you create a redundant index on the table for this method to match against. This creates an amazing amount of waste not only of storage space, but also performance. This method would allow so many more use cases if it simply let you describe the conflict you want to match against.

More advanced method:

class ApplicationRecord
  def self.bulk_insert(array_of_hashes, conflict_targets = Array(primary_key))
    columns = array_of_hashes.first.keys
    values = array_of_hashes.flat_map(&:values)
    rows = array_of_hashes.map do |f|
      "(#{columns.size.times.map { "?" }.join(", ")})"
    end.join(", ")

    sql = sanitize_sql_array([<<~SQL, *values])
      INSERT INTO "#{table_name}"
      (#{columns.map { |c| "\"#{c}\"" }.join(",")})
      VALUES #{rows}
      ON CONFLICT (#{conflict_targets.map { |c| "\"#{c}\"" }.join(", ")}) DO NOTHING
    SQL

    connection.execute(sql.squish)
  end
end

SQL it produces:

User.bulk_insert([{email: "a@example.com"}, {email: "b@example.com"}])
INSERT INTO "users" ("email") VALUES ('a@example.com'), ('b@example.com') ON CONFLICT ("id") DO NOTHING

This would then allow you to reference any conflict you like:

alter table users add unique (email);
User.bulk_insert(
  [{email: "a@example.com"}, {email: "b@example.com"}],
  %i[email]
)

Check if a form is valid

You can check if a form passes HTML validations with javascript using the checkValidity function:

<form name="login">
 <fieldset>
   <legend>Email</legend>
   <input type="email" name="email" required />
 </fieldset>

 <fieldset>
   <legend>Password</legend>
   <input type="password" name="password" required />
 </fieldset>

 <div style="margin-top: 1rem">
   <input type="submit" value="login" />
   <button type="button" id="validate">
    validate
   </button>
 </div>
</form>

<script>
  const button = document.querySelector("#validate")
  if (button) {
    button.addEventListener("click", function() {
      alert(document.forms.login.checkValidity() ? "Form is valid" : "INVALID");
    });
  }
</script>

The difference between %w and %W in Ruby

%w can construct space delimited word arrays like this

%w(my cool word array)
#=> ["my", "cool", "word", "array"]

%W works similarly, however it offers ways to interpolate with variables and escape special characters in the assignment, while %w does not.

street_name = 'Sesame Street'
%W(I live on #{street_name})
#=> ["I", "live", "on", "Sesame Street"]

Sort array of numbers

In javascript, the Array sort function will cast everything to a string. So when you have an array of numbers, you need to repetitively cast them to numbers:

[1080, 720, 480].sort()
=> [1080, 480, 720]

So you need have to write your own sort function (don't mess it up!)

[1080, 720, 480].sort((a, b) => Number(a) - Number(b))
=> [480, 720, 1080]




🐉

Limit Jest Test Coverage to Specific Files

Jest allows you to view test coverage when it runs.

But even if I only run tests for a specified file or files, the --coverage output will include all files.

$ jest src/directoryToTest --coverage

If your app is large, this can generate a lot of output and be difficult to parse. If I only care about the coverage for files in directoryToTest, I can filter the output of --coverage with --collectCoverageFrom=:

$ jest src/directoryToTest --coverage --collectCoverageFrom="src/directoryToTest/**"

docs

SVG using href must be served from same site url

We like to use SVG sprites

However, if you utilize a cdn or serve assets from an external source... you may run into some issues. Hopefully you find this and it saves you time that we lost figuring this out

According to MDN:

For security reasons, browsers may apply the same-origin policy on use elements and may refuse to load a cross-origin URL in the href attribute. There is currently no defined way to set a cross-origin policy for use elements.

Remove Padding from Postgres Formatting Functions

Earlier today, I was trying to join a table on a formatted string row, but wasn't getting the results I expected. Turns out that my formatting string had blank padding and I discovered "fill mode".

When using postgres formatting functions, like to_char, some of the formatting options include padding in the result. For example, the day format string will be blank padded to 9 chars.

select to_char(current_date, 'day');

  to_char
-----------
 sunday

You can use the "fill mode" (FM) option to remove any leading zeros or blank padding characters by prepending it to your format option:

select to_char(current_date, 'FMday')

 to_char
---------
 sunday

https://www.postgresql.org/docs/current/functions-formatting.html

Find Unused Cucumber Step Definitions

One of the challenges of using cucumber is properly managing your step definitions. Left unchecked, you will eventually have many unused steps. It's extremely cumbersome to prune these manually. Luckily, you can use cucumber's -f / --format flag to get feedback on unused step_definitions and their locations:

bundle exec cucumber --dry-run --format=stepdefs

If your step definition is unused, it will be annotated with a line under that says NOT MATCHED BY ANY STEPS. See the example -

/^I submit the proposal request form$/     # features/step_definitions/contact_steps.rb:39
  NOT MATCHED BY ANY STEPS

Parsing nested string json

Today I learned how to parse a nested json with jq, but the nested json is a string. It's just easier to show an example so here we are:

{
  "a": "{\"b\": \"c\"}"
}

This is not a common situation but I found that out today on a codebase and my first thought was to call jq get the content of the node a and then pipe it into another jq command. It would look like this:

echo '{"a": "{\\"b\\": \\"c\\"}"}' | jq '.a' | jq
# => "{\"b\": \"c\"}"

As we can see the result is not a json, but a string so we cannot access inner nodes just yet.

And the solution to this problem is to use the -r flag on the first jq call to output the result in a raw format, so the " surounding double quotes will disappear. And with that in place we can easily parse the nested/nasty json:

echo '{"a": "{\\"b\\": \\"c\\"}"}' | jq -r '.a' | jq
# => {
# =>   "b": "c"
# => }

Then finally:

echo '{"a": "{\\"b\\": \\"c\\"}"}' | jq -r '.a' | jq '.b'
# => "c"

Direct many-to-many ActiveRecord associations

By using has_and_belongs_to_many you can directly relate models with a many-to-many-association. For example, if you have a model Film and a model Producer, a film can have multiple :producers, and a Producer can have multiple :films; in this case, each of models could have a has_and_belongs_to_many association with the other.

#film.rb
class Film < ApplicationRecord
  has_and_belongs_to_many :producers
end

#producer.rb
class Producer < ApplicationRecord
  has_and_belongs_to_many :films
end
# Now associative records can be stored and retrieved
film = Film.where(title: "The Irishman")
producer = Producer.where(name: "Martin Scorcese")
film.producers << producer
film.save

producer.films
#=> This should return a collection including The Irishman
films.producers
#=> This should return a collection including Martin Scorcese

Confirmation alert using Turbo in Rails

Using an confirmation browser alert is a common thing to do. This seemingly works fine while using Turbo, however, try to cancel the alert and your request still goes through.

Previously on rails:

<%= link_to "Go away", "/go-away", data: {confirm: "Are you sure?"} %>

Use data-turbo-confirm for correct functionality:

<%= link_to "Go away", "/go-away", data: {turbo_confirm: "Are you sure?"} %>

Pattern Match Keyword List in Function Def

TIL that you can pattern match a keyword list in a function definition.

Sometimes you'll receive the last argument as an empty keyword list; in this case, we're calling it opts. You can pattern match key-values by defining the shape in a function.

  defmodule Example do
    def hello(opts \\ [])
 
   # will match when message is the only keyword included in opts
    def hello([message: message]) do
      IO.puts("Hello #{message}")
    end

    # will match when there are multiple keywords but message is the first
    def hello([{:message, message} | _rest] = opts) do
      IO.puts("Hello #{message}")
    end
  end

> Example.hello(message: "World")
Hello World
:ok

# could also call in the more verbose way
> Example.hello([message: "World"])
Hello World
:ok

# :message but with other args after
> Example.hello(message: "World", something_else: "hi")
Hello World
:ok

Note: As a TIL reader pointed out, pattern matching Keywords will make your function args order dependent. The following would not work:

> Example.hello(foo: "bar", message: "World")
** (FunctionClauseError) no function clause matching in Example.hello/1 

If you need them to be order independent, use a map or just match on single argument, then check for each option appropriately with the Keyword module.

https://elixir-lang.org/getting-started/keywords-and-maps.html

Creating Custom Typescript Types

In TypeScript you can build your own custom object types. Custom types work just like any other type. You can use it like this:

type Vehicle = {
    make: string, 
    model: string, 
    capacity: number, 
}

//now we can use the vehicle type in a definition
const corolla: Vehicle = {
    make: "Toyota",
    model: "Corolla", 
    capacity: 5,
}

If you define a vehicle without any of the required types, TypeScript will provide an error stating which property is missing from the object. For example:

const corolla: Vehicle = {
    make: "Subaru",
    model: "Outback", 
}

This definition will provide an error Property 'capacity' is missing in type '{ make: string; model: string; }' but required in type 'Vehicle'., because the capacity property is missing.

Ruby memoization with nil values

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

The Outlet of Rails Stimulus

Besides values and targets Rails Stimulus has now outlets. Now we can invoke functions from one controller to the other like that:

// result_controller.js
export default class extends Controller {
  markAsSelected(event) {
    // ...
  }
}
// search_controller.js
export default class extends Controller {
  static outlets = [ "result" ]

  selectAll(event) {
    this.resultOutlets.forEach(result => result.markAsSelected(event))
  }

Encrypting database columns with Rails 7

In Rails 7, Active Record includes the option to encrypt database columns. To start run bin/rails db:encryption:init, then copy the resulting keys to your app's credentials.yml. Now in your model, you can tell Active Record to encrypt a column by using the encrypts which takes a db column name as an argument. For example:

class User < ApplicationRecord
  encrypts :super_secret_data
end

Active record will automatically decrypt the data upon retrieval. See more here.

!important property

If you ever find yourself with conflicting rules on an element, and want one to take precedence even over rules with higher specificity. you can add the !important property, to override all other rules.

Here is an example:

#id {
  background-color: blue;
}

.class {
  background-color: gray;
}

p {
  background-color: red !important;
}
&lt;p>This is some text in a paragraph.&lt;/p>

&lt;p class="class">This is some text in a paragraph.&lt;/p>

&lt;p id="id">This is some text in a paragraph.&lt;/p>

In this case, all of these &lt;p> tags will have a red background.

Javascript private class functions

Today I learned that you can make a class function to be private by prefixing it with a #. Check this out:

class MyClass {
  myPublicFunc() {
    console.log("=> hey myPublicFunc");
    this.#myPrivateFunc();
  }

  #myPrivateFunc() {
    console.log("=> hey myPrivateFunc");
  }
}

const myClass = new MyClass();

myClass.myPublicFunc();
// => hey myPublicFunc
// => hey myPrivateFunc

myClass.#myPrivateFunc();
// Uncaught SyntaxError:
//   Private field '#myPrivateFunc' must be declared in an enclosing class

We can also use that trick to make static methods, fields or static fields as well.

Thanks Andrew Vogel for this tip 😁