Logo

TODAY I LEARNED

Strip and downcase email addresses if you're using them as lookup keys for anything

Seems like something I need to learn every few years, and recently popped up on one of my side projects. If you're relying on email addresses as lookup keys, for instance in authentication/login scenarios, then standardize on lowercase and strip whitespace for good measure.

This came up when a customer was not able to get inbound email processing to work for his account. Turns out that his user record had a properly downcased email, but his SMTP server was sending his email address with capitalization. Some string massaging fixed the immediate issue.

User.find_by(email: reply_params["sender"].to_s.downcase)

After putting this fix in, I found everywhere else in the system that email addresses come in as input and gave them the same treatment.

Learned by obie-fernandez on Feb 17, 2021

973983 adforcodereview v3 0211021 c02 021121

Migration operation that should run only in one direction

Disclaimer: I know it's not recommended to do data mutation in schema migrations. But if you want to do it anyway, here's how you do a one-way operation, using the reversible method.

class AddAds < ActiveRecord::Migration[5.0]
  def change
    create_table :ads do |t|
      t.string :image_url, null: false
      t.string :link_url, null: false
      t.integer :clicks, null: false, default: 0
      t.timestamps
    end

    reversible do |change|
      change.up do
        Ad.create(image_url: "https://www.dropbox.com/s/9kevwegmvj53whd/973983_AdforCodeReview_v3_0211021_C02_021121.png?dl=1", link_url: "http://pages.magmalabs.io/on-demand-github-code-reviews-for-your-pull-requests")
      end
    end
  end
end

Learned by obie-fernandez on Feb 16, 2021

973983 adforcodereview v3 0211021 c02 021121

UGC enhancing the customer experience

Customer experience has never been more critical. Consumers crave enjoyable experiences with brands that are memorable. User-generated content (UGC) ticks all the boxes when it comes to connective content:

  • It builds and strengthens communities

  • It’s relatable and uplifting

  • It enables brands to meet customers where they’re already hanging out

  • It helps brands generate tons more content against a backdrop of stay-at-home orders and restrictive measures

UGC will be a common theme in 2021 as a significant content resource.

Learned by juan-castillo on Feb 16, 2021

Lotties with Rails 6 and Webpacker

1) Install Lottie Player:

npm install --save @lottiefiles/lottie-player

2) Require it at app/javascript/packs/application.js

require('@lottiefiles/lottie-player');

3) Set webpacker to load jsons at config/webpacker.yml

static_asset_extensions:
  - .json

4) Put your lotties jsons wherever you want e.g. app/javascript/images/lotties

5) Render lottie-player tag in your htm

%lottie-player{ autoplay: true,
                loop: true,
                src: asset_pack_path('media/images/lotties/mylottie.json') }

6) Profit

Learned by carlos-muniz on Feb 15, 2021

973983 adforcodereview v3 0211021 c02 021121

Period of Time with Ruby on Rails and Integers | ActiveSupport::Duration

From the rails console try next:

irb(main):001:0> period_of_time = 10.minutes
=> 10 minutes
irb(main):002:0> period_of_time.class
=> ActiveSupport::Duration
irb(main):003:0> period_of_time = 10.hours
=> 10 hours
irb(main):004:0> period_of_time.class
=> ActiveSupport::Duration
irb(main):005:0> period_of_time.to_i
=> 36000

Useful when you work with devise authentication gem, e.g to expire the session in a certain period of time.

class User < ApplicationRecord
  devise :database_authenticatable, :timeoutable,
    timeout_in: ENV['EXPIRATION_TIME_IN_MINUTES'].to_i.minutes || 10.minutes
end

And so on...

User.where(created_at: 20.days.ago..10.minutes.ago)

Learned by emmanuel-oseguera on Feb 15, 2021

973983 adforcodereview v3 0211021 c02 021121

Styling broken links

<img src="http://bitsofco.de/broken.jpg" alt="Broken Image">
img {
  font-family: 'Helvetica';
  font-weight: 300;
  line-height: 2;  
  text-align: center;

  width: 100%;
  height: auto;
  display: block;
  position: relative;
}

img:before { 
  content: "We're sorry, the image below is broken :(";
  display: block;
  margin-bottom: 10px;
}

img:after { 
  content: "\f1c5" " " attr(alt);

  font-size: 16px;
  font-family: FontAwesome;
  color: rgb(100, 100, 100);

  display: block;
  position: absolute;
  z-index: 2;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background-color: #fff;
}

See the Pen Styling Broken Images by Victor Velazquez (@vicmaster) on CodePen.

Original source: bitsofco.de

Learned by Victor Velazquez on Feb 13, 2021

Connecting Ruby to AWS IoT Core using MQTT client

If you need to use Ruby to connect to Aws Iot Core, this is all you need:

require 'aws-sdk-iot'
require 'aws-sdk-secretsmanager'
require 'json'
require 'mqtt'

secrets_manager = Aws::SecretsManager::Client.new(
    region: ENV["IOT_AWS_REGION"],
    access_key_id: ENV["IOT_AWS_ACCESS_KEY"],
    secret_access_key: ENV["IOT_AWS_SECRET_ACCESS_KEY"]
    )

client = Aws::IoT::Client.new(
    region: ENV["IOT_AWS_REGION"],
    access_key_id: ENV["IOT_AWS_ACCESS_KEY"],
    secret_access_key: ENV["IOT_AWS_SECRET_ACCESS_KEY"]
    )

# Creates new ssl certificate
cert = client.create_keys_and_certificate(set_as_active: true)

# A policy named iot-mqtt needs to exist with permissions to publish and read
# any topic names
client.attach_policy(policy_name: "iot-mqtt", target: cert.certificate_arn)

# Stores the certificate in aws secrets manager
secrets_manager.create_secret(name: "iot_cert_pem", secret_string: cert.certificate_pem)
secrets_manager.create_secret(name: "iot_private_key", secret_string: cert.key_pair.private_key)

# Reads the certificate from aws secrets manager
cert_pem = secrets_manager.get_secret_value(secret_id: "iot_cert_pem").secret_string
private_key = secrets_manager.get_secret_value(secret_id: "iot_private_key").secret_string

# Connects to aws iot core endpoint using mqtts
mqtt_client = MQTT::Client.new(ENV["IOT_AWS_ENDPOINT"])
mqtt_client.cert = cert_pem
mqtt_client.key = private_key
mqtt_client.connect(MQTT::Client.generate_client_id("my-awesome-app-"))

# Publishes a message
message = { desired: { speed_limit: 35 } }
mqtt_client.publish("$aws/things/sensor_home/shadow/update", { state: message }.to_json)

# Listens to all accepted shadow updates
mqtt_client.get("$aws/things/+/shadow/+/accepted") do |topic, message|
    payload = JSON.decode(message)
    puts "Got #{topic}"
    puts "With #{payload}"
end

Learned by Edwin Cruz on Feb 11, 2021

973983 adforcodereview v3 0211021 c02 021121

How to add timeouts to slow queries

Sometimes some of your queries are taking too long to execute; you can specify optimizer hints and define timeouts for those queries.

Employee.optimizer_hints("MAX_EXECUTION_TIME(5000)").all

It will raise a StatementTimeout exception if the query takes longer than usual to execute

Example (for PostgreSQL with pg_hint_plan):

Employee.optimizer_hints("SeqScan(employees)", "Parallel(employees 8)")

Example (for MySQL):

Employee.optimizer_hints("MAX_EXECUTION_TIME(50000)", "NO_INDEX_MERGE(employees)")

There are many causes for sudden slow queries in many databases, such as missing index, wrong catching, and performance.

But this is a topic for another day!

Learned by Victor Velazquez on Feb 11, 2021

Monthly streak display using SQL CTE in Rails

How to make a streak display like 750words.com

image

The database can do the heavy lifting for this kind of thing. A CTE (Common Table Expression) in a WITH clause to give me the set of days of the month that I would fill in with the completion data using a LEFT JOIN to make sure I got rows back even when there was no corresponding data on the right side.

# app/models/streak.rb
Streak = Struct.new(:user, :date) do
  # If you're wondering why we didn't use class extension syntax of Struct see the following link
  # https://tiagoamaro.com.br/2016/03/05/superclass-mismatch-structs-and-unicorn/
  SQL = "WITH dates(d) AS (
            SELECT generate_series(
              (date ?)::timestamp,
              (date ?)::timestamp,
              interval '1 day')
            )
            SELECT dates.d::date created_at, count(e.id) count FROM dates
            LEFT JOIN entries e on dates.d::date = e.created_at::date AND e.user_id = ?
            GROUP BY dates.d::date
            ORDER BY dates.d::date"


  def calendar
    Entry.find_by_sql([SQL, date.beginning_of_month, date.end_of_month, user.id])
  end
end

I'm knowingly abusing ActiveRecord by shoving the data I want to represent into Entry objects. I can be kinder to my future self and other maintainers by subsequently wrapping the returned objects into something more descriptive like StreakMonth or whatever.

Learned by obie-fernandez on Feb 10, 2021

973983 adforcodereview v3 0211021 c02 021121

Stubbing a block in rspec

There're at least two ways to stub a block in rspec, the first version is using and_yield

config = double('Config', enabled: true)
allow(app).to receive(:config).and_yield(config)
app.config do |config|
  expect(config.enabled).to be_truthy
end

The second version is receiving the block

config = double('Config', enabled: true)
allow(app).to receive(:config) do |_, &block|
  block.call(config)
end|
app.config do |config|
  expect(config.enabled).to be_truthy
end

Learned by Edwin Cruz on Feb 10, 2021

973983 adforcodereview v3 0211021 c02 021121