Logo

TODAY I LEARNED

More about randomness

If you want to generate random but predictable sequences of numbers, then the rand command and the srand are not enough, you have to use a trick to save the state of the variable. Fibers are primitives for implementing light weight cooperative concurrency in Ruby. Basically they are a means of creating code blocks that can be paused and resumed, much like threads. The main difference is that they are never preempted and that the scheduling must be done by the programmer and not the VM.

require 'rspec'

#i = pseudo_random 10
#p i.resume => 37
#p i.resume => 12
#p i.resume => 72
#
def pseudo_random num
  srand 1

  fiber = Fiber.new do
    num.times do
      Fiber.yield rand 100
    end
  end
end


describe 'Pseudo Random number generator' do
  it 'creates the same sequence of random numbers' do
    random_sequence = pseudo_random 3
    expect(random_sequence.resume).to eq(37)
    expect(random_sequence.resume).to eq(12)
    expect(random_sequence.resume).to eq(72)
  end
end

Learned by enrique-meza on Mar 10, 2021

973983 adforcodereview v3 0211021 c02 021121

Quasi-Random numbers in Ruby

I was interested in random sequence because I was in need to test the Montecarlo Method for getting Pi digits.

One method to estimate the value of π (3.141592…) is by using a Monte Carlo method. This method consists of drawing on a canvas a square with an inner circle. We then generate a large number of random points within the square and count how many fall in the enclosed circle. Pi

So, if you need a random sequence, you can use Sobol for quasi-random numbers.


require 'gsl'

q = GSL::QRng.alloc(GSL::QRng::SOBOL, 2)
v = GSL::Vector.alloc(2)
for i in 0..1024 do
  q.get(v)
  printf("%.5f %.5f\n", v[0], v[1])
end

Learned by enrique-meza on Mar 10, 2021

Updating ActiveRecord models without loading an instance using Postgresql

There're sometimes that we need to update an ActiveRecord Model but it is not necesary to load an instance, the normal flow would be the following:

profile = UserProfile.find_by(user_id: id)
profile.update(last_seen_at: Time.now)

The problem with this is that we load a useless instance of UserProfile, it is not needed, in a high traffic sites, an extra select query can count a lot, but luckly, Rails has addressed this with upsert command:

UserProfile.upsert({ last_seen_at: Time.now, user_id: id }, unique_by: :user_id)

This will use native Postgresql upsert command to update a record if it exists or insert a new one and no select will be performed, everything in a single query instead of two.

To make it really work, you need to modify your created_at and updated_at columns to have default current_timestamp

Learned by Edwin Cruz on Mar 10, 2021

973983 adforcodereview v3 0211021 c02 021121

NIX node manager

Use n, an extremely simple Node version manager that can be installed via npm.

Say you want Node.js v12.10.0 to build Ghost template.

npm install -g n   # Install n globally
n 12.10.0          # Install and use v12.10.0
Usage:
n                            # Output versions installed
n latest                     # Install or activate the latest node release
n stable                     # Install or activate the latest stable node release
n <version>                  # Install node <version>
n use <version> [args ...]   # Execute node <version> with [args ...]
n bin <version>              # Output bin path for <version>
n rm <version ...>           # Remove the given version(s)
n --latest                   # Output the latest node version available
n --stable                   # Output the latest stable node version available
n ls                         # Output the versions of node available

Learned by Victor Velazquez on Mar 10, 2021

973983 adforcodereview v3 0211021 c02 021121
973983 adforcodereview v3 0211021 c02 021121

Using Rails to migrate columns from JSON to JSONB in Postgresql

Postgres offers data type json to store any structure easily, but one dissadvantage is that filtering by properties stored in the json column are super slow, one simple fix before refactoring the whole implementation is to migrate the column to be jsonb, since it is stored in binary form, it supports indexes, a easy and safe way to do it is as follows:

class ModifyJSONDataDataType < ActiveRecord::Migration[6.0]
  def up
    add_column :table_name, :data_jsonb, :jsonb, default: '{}'

    # Copy data from old column to the new one
    TableName.update_all('data_jsonb = data::jsonb')

    # Rename columns instead of modify their type, it's way faster
    rename_column :table_name, :data, :data_json
    rename_column :table_name, :data_jsonb, :data
  end

  def down
    safety_assured do
      rename_column :table_name, :data, :data_jsonb
      rename_column :table_name, :data_json, :data
    end
  end
end

Then, using another migration(due the ability to disable transactions), add an index to it

class AddIndexToDataInTableName < ActiveRecord::Migration[6.0]
  disable_ddl_transaction!

  def change
    add_index :table_name, :data, name: "data_index", using: :gin, algorithm: :concurrently

    # You can even add indexes to virtual properties:
    # add_index :table_name, "((data->'country')::text)", :name => "data_country_index", using: 'gin', algorithm: :concurrently
  end
end

Learned by Edwin Cruz on Mar 8, 2021

973983 adforcodereview v3 0211021 c02 021121

The slice_when method in Ruby

Creates an enumerator for each chunked elements. The beginnings of chunks are defined by the block.

This method splits each chunk using adjacent elements, elt_before, and elt_after, in the receiver enumerator. This method split chunks between elt_before and elt_after where the block returns true.

The block is called the length of the receiver enumerator minus one.

The result enumerator yields the chunked elements as an array. So each method can be called as follows:

enum.slice_when { |elt_before, elt_after| bool }.each { |ary| ... }

For example:

Return adjacent elements from this array [1, 2, 3, 5, 6, 9, 10] in chunked elements as an array.

[1, 2, 3, 5, 6, 9, 10].slice_when {|i, j| i+1 != j }.to_a
=> [[1, 2, 3], [5, 6], [9, 10]]

Learned by Victor Velazquez on Mar 5, 2021

The each_cons method in Ruby

Iterates the given block for each array of consecutive <n> elements. If no block is given, returns an enumerator.

irb(main):001:0> [1,2,3,4].each_cons(2).to_a
=> [[1, 2], [2, 3], [3, 4]]

irb(main):002:0> [1, 2, 3, 5, 6, 9, 10].each_cons(2).to_a
=> [[1, 2], [2, 3], [3, 5], [5, 6], [6, 9], [9, 10]]

Print any two adjacent words in a given text:

def print_adjacent_words(phrase)
   phrase.split.each_cons(2) do |words|
     puts adjacent_word = words.join(" ")
   end
end
irb(main):025:0> print_adjacent_words("Hello Darkness my old friend")
Hello Darkness
Darkness my
my old
old friend
=> nil

Learned by Victor Velazquez on Mar 5, 2021

973983 adforcodereview v3 0211021 c02 021121

Optional Chaining Operator (?) and Nullish Coalescing Operator (??)

Optional Chaining Operator(?) (**)

//Car object
const car = {
  attributes: {
   year: 2021,
   model: "Ferrari"
  }
}

Before in javascript to validate that the attributes of an object exist, it was necessary to use the And operator (&&) to validate if the attributes of the object existed.

See the following example:

if (car && car.attributes && car.attributes.model) {
  console.log('Exist',  car.attributes.model)
} else {
  console.log('Do not exist')
}

# => "Exist Ferrari"

Now with Optional Chaining Operator (?) We can validate if the property exit will return undefined if the property attributes or model doesn’t exist.

if (car?.attributes?.model) {
  console.log('Exist',  car.attributes.model)
} else {
  console.log('Do not exist')
}

# => "Exist Ferrari"

Nullish Coalescing Operator(??) (**)

//Car object
const car = {
  attributes: {
   year: 2021,
   model: "Ferrari"
  }
}

If we want to get the car model then we would like below:


console.log(car.attributes.model);
# => "Ferrari"

Now the new car object does not have the model property, so we get an undefined value

//Car object
const car = {
  attributes: {
   year: 2021,
  }
}
console.log(car.attributes.model);
# => "undefined"

To avoid the undefined value the OR (| |) operator can be used, It will now return the value BMW because the property model is undefined. But what if the name property is empty like below example

console.log(car.attributes.model | | "BMW" );
# => "BMW"

Now the problem is when the property model exists but has an empty, undefined or null value we will get the BMW value and this is not right.

//Car object
const car = {
  attributes: {
   year: 2021,
   model: ""
  }
}
console.log(car.attributes.model | | "BMW" );
# => "BMW"

The solution for this problem is to use the Nullish Coalescing Operator (??), Which will print the result as an empty string.

console.log(car.attributes.model ?? "BMW" );
# => ""

Learned by leyaim-jimenez on Mar 4, 2021

Ruby Double star (**)

def hello(a, *b, **c)
  return a, b, c
end

a is a regular parameter. *b will take all the parameters passed after the first one and put them in an array. **c will take any parameter given in the format key: value at the end of the method call.

See the following examples:

One parameter

hello(1)
# => [1, [], {}]

More than one parameter

hello(1, 2, 3, 4)
# => [1, [2, 3, 4], {}]

More than one parameter + hash-style parameters

hello(1, 2, 3, 4, a: 1, b: 2)
# => [1, [2, 3, 4], {:a=>1, :b=>2}]

Learned by Victor Velazquez on Mar 4, 2021

973983 adforcodereview v3 0211021 c02 021121

THE MEANING AND POWER OF WORDS (ONLINE)

If the interpretation of messages in an oral face-to-face conversation is too subjective —even with clear tones of voice and body language— imagine when communicating through a screen! We have to be assertive AF!   I believe that to be assertive through texts messages we need to consider WRITING RULES such as spelling (correct use of punctuation marks, accents, and capital letters), syntax (the way in which words and the groups they form are combined to express meanings, as well as the relationships established between all these units), and semantics (the meaning and changes in the meaning of words and expressions).   To the point> Semantically speaking, it is important to consider that there are two divisions in this area: denotation and connotation.   The denotation: This is the original or formally accepted expression of the word, this is the word that is formally found in encyclopedic dictionaries and is universally accepted.   Connotation: Connotation is the alternate or secondary form in which a word is used, such as is the case with the word donkey, which in denotative form implies the equine type animal, and in the connotative form the dumb man or person.   Get it? So, we have to be super conscious of the meaning and power of the words we choose when having written conversations. Is like: the connotation of the words OCUPO and NECESITO could be similar, but the correct thing to say, because of the denotation of the words, is NECESITO QUE ME AYUDES ME ACORREGIR ESTE CODIGO.   It is better that way so we don't misunderstand things. And if so, nothing better than talking to eliminate misunderstandings, right? Talking-communicating is the best way to understand each other, the best way to organize ourselves.   It is good to be sincere and direct. But, we must try to be assertive. And, in my opinion, there is a fine line between being assertive, sincere, or direct > and rude. To be assertive the message must get through to the receiver correctly, otherwise, it's not.

momazo

Learned by graciela-virgen on Mar 3, 2021

973983 adforcodereview v3 0211021 c02 021121
973983 adforcodereview v3 0211021 c02 021121

Optional Chaining in JavaScript

The Optional Chaining Operator ? allows you to read the value of a property located deep within an object without having to validate each property in the object. It works in a similar way than the chaining operator ., except that instead of causing an error if a reference is nullish, the expression will return a value of undefined.

A common way to validate properties was to do something like this:

const player = {
  stats: {
    stamina: 40
  }
}

let stamina
if (player && player.stats) {
  stamina = player.stats.stamina
}

This was done because calling player.stats.stamina directly could led into an error being thrown if stats didn't exists, and sometimes even the execution would stop.

Using the Optional Chaining Operator, we could do something like this:

const player = {
  stats: {
    stamina: 40
  }
}

const stamina = player.stats?.stamina

The result is shorter and simpler, and in case stats didn't exists the value would be undefined, without an error being thrown.

Learned by ricardo-santoyo on Mar 1, 2021

973983 adforcodereview v3 0211021 c02 021121

Remove Rails model's fields without crashing on production

If you are experiencing problems after removing an attribute from a Rails model it is probably because ActiveRecord has cached that field, it is something normal is intended to help to improve performance on production environments but when removing a field may cause errors because Rails is training to read/writer a column that doesn't exist anymore, so for Rails 5 and newest we have the ignore_columns setting to fix this issue, simply add it to your model something like:

class MyModel < ApplicationRecord
  self.ignore_columns = %w[field_to_ignore]
end

And you are good to go! if you want to be warned or remind to your team you would like to consider using the strong migrations gem https://github.com/ankane/strong_migrations

Learned by ozmar-ugarte on Mar 1, 2021

973983 adforcodereview v3 0211021 c02 021121

Native Pub/Sub in Rails with ActiveSupport::Notifications

If you want to use pub/sub design pattern inside your rails app, there's no need to add extra dependencies, you can use ActiveSupport::Notifications to do the job

Example:

class Order
  # methods

  def complete
    update(complted_at: Time.zone.now, etc: 'some')
    ActiveSupport::Notifications.instrument("order_completed", { number: order.number })
  end
end

module Subscribers
  class SendConfirmationEmail
    def self.subscribe!
      ActiveSupport::Notifications.subscribe("order_completed") do |_name, _start, _finish, _id, params|
        order = Order.find_by number: params[:number]
        OrderMailer.confirm_order(order).deliver_later
      end
    end
  end
end

module Subscribers
  class UpdateCustomerCRM
    def self.subscribe!
      ActiveSupport::Notifications.subscribe("order_completed") do |_name, _start, _finish, _id, params|
        order = Order.find_by number: params[:number]
        CrmIntegration.update_customer(order.customer.email, order.total_amount)
      end
    end
  end
end

# etc

Learned by Edwin Cruz on Mar 1, 2021

973983 adforcodereview v3 0211021 c02 021121

Measuring memory utilization

Ruby comes with the cool module Benchmark, but the only downside is that it only provides the time taken for each task.

A simple approach to get how many memory the ruby process is consuming is to call: ps -o rss:

memory_before = `ps -o rss= -p #{$$}`.to_i
Utils.heavy_memory_consumming_process
memory_after = `ps -o rss= -p #{$$}`.to_i

puts "The process took #{memory_after - memory_before} KB"

Learned by Edwin Cruz on Mar 1, 2021

973983 adforcodereview v3 0211021 c02 021121

THE HOOK IS IN EXPERIENCE

When working with brands, yep, its image and its communication are essential. But, TODAY I LEARNED: EXPERIENCE IS THE REAL HOOK!! People will forget what you say, people will forget what you do, but they will never forget HOW YOU MAKE THEM FEEL!!! Right???

In life, sometimes I tend to observe and analyze stuff that would help me to improve something, especially at work. There is a local fashion brand I used to follow (as a fan, I must say) because -besides their clothes are cute- I loved their branding and how they work with their social media communication. Well, I am working with my own fashion brand and I tend to look at the mentioned one as a visual reference. But I wanted to know how their logistics works and I tried to buy something to learn about it, BUT THE EXPERIENCE WAS TERRIBLE! They made me feel ignored and even stupid with their election of some words...

I must say this post is not about talking sh** about a brand and complain about it. Is about expose a case so we can understand why EXPERIENCE is the real value. Even if you have the most beautiful, luxurious, good vibing brand, if you gave a negative experience YOU WILL LOST a follower and probably A CLIENT!

Consumers will transmit their experience: positive or negative. It could become viral and have a huge impact. So, if you really want to capitalize with it, GIVE THE BEST EXPERIENCE EVER TO YOUR AUDIENCE! You could even create brand ambassadors by doing it!

xoxo

Learned by graciela-virgen on Feb 25, 2021

973983 adforcodereview v3 0211021 c02 021121

Create a Pull Request from the CLI

In 2020 GitHub launched its CLI.
Among many other wonders, it allows you to create a PR from the command line.
Using it is exceedingly easy!

First, install it
$ brew install gh
Then, authenticate
$ gh auth login
Finally, create a PR with
$ gh pr create
If your repo has a PR template, it will make use of it.

Learned by Eduardo Gutiérrez on Feb 23, 2021

973983 adforcodereview v3 0211021 c02 021121

Use discard_on to discard the job with no attempts to retry

Discard the job with no attempts to retry, if the exception is raised. This is useful when the subject of the job, like an Active Record, is no longer available, and the job is thus no longer relevant.

You can also pass a block that'll be invoked. This block is yielded with the job instance as the first and the error instance as the second parameter.

Example: 1

class SearchIndexingJob < ActiveJob::Base
  discard_on ActiveJob::DeserializationError
  discard_on(CustomAppException) do |job, error|
    ExceptionNotifier.caught(error)
  end

  def perform(record)
    # Will raise ActiveJob::DeserializationError if the record can't be deserialized
    # Might raise CustomAppException for something domain specific
  end
end

Example: 2:

class UserNotFoundJob < ActiveJob::Base
  discard_on ActiveRecord::RecordNotFound



  def perform(user_id)
    @user = User.find(user_id)
    @user.do_some_thing
  end
end

Source

Learned by Victor Velazquez on Feb 20, 2021

973983 adforcodereview v3 0211021 c02 021121

When to eager load relationships in Rails and when it is not that good

Rails provides a way to eager load relationships when fetching objects, the main idea is to avoid queries N+1, but, when isn't a good idea when to do it?

Good

When rendering unique results that can not be cached, for example: table reports

Why? Most of the times you need to display related information

orders = Order.includes(:user, :line_items).completed

Try to avoid

When you use fragment cache

Why? Eager load is executed before the rendering, regardless the final result is already cached or not. If using eager loading, it will always be executed, but when allowing queries n+1 that query will be executed once to fill the cache, and that's it

products = Product.includes(:categories, variants: [:price]).search(keywords)

Use product.id & updated_at to fill a fragment cache and fetch the data from database only when needed, no extra info needed such as variants, categories, prices, etc

Learned by Edwin Cruz on Feb 19, 2021

Test Rails log messages with RSpec

Have you wondered how to test your Rails log messages?

Try this:

it "logs a message" do
  allow(Rails.logger).to receive(:info)
  expect(Rails.logger).to receive(:info).with("Someone read this post!")

  visit root_path

  expect(page).to have_content "Welcome to TIL"
end

Or this:

it "logs a message" do
  allow(Rails.logger).to receive(:info)

  visit root_path

  expect(page).to have_content "Welcome to TIL"
  expect(Rails.logger).to have_received(:info).with("Someone read this post!")
end

Source

Learned by Victor Velazquez on Feb 18, 2021

973983 adforcodereview v3 0211021 c02 021121

Beware of calling #count on Active Record relations!

Given code like this:

records = Record.includes(:related).all # Eager-loads to prevent N+1 queries...
records.each do |record|
  puts record.related.count # => ... but this produces N+1 queries anyway!
end

If you run this, you'll notice you get an N+1 queries problem, even though we're using #includes. This happens because of record.related.count. Remember, records.related here is not an Array but an instance of CollectionProxy and its #count method always reaches out to the database. Use #length or #size instead to solve this issue.

records = Record.includes(:related).all
records.each do |record|
  puts record.related.length # Problem solved!
end

Learned by kevin-perez on Feb 18, 2021

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