Logo

TODAY I LEARNED

Fr Unit with CSS Grid Layout

I read about CSS Grid Layout, and I found the Fr unit, so I have to try it out.

From w3.org:

Flexible Lengths: the fr unit A flexible length or is a dimension with the fr unit, which represents a fraction of the leftover space in the grid container. Tracks sized with fr units are called flexible tracks as they flex in response to leftover space similar to how flex items with a zero base size fill space in a flex container.

The distribution of leftover space occurs after all non-flexible track sizing functions have reached their maximum. The total size of such rows or columns is subtracted from the available space, yielding the leftover space, which is then divided among the flex-sized rows and columns in proportion to their flex factor.

Each column or row’s share of the leftover space can be computed as the column or row’s * / .

Read more about fr unit

See this example:

CSS:

.grid-container { 
  max-width: 100%; 
  margin: 3em auto; 
  display: grid; 
  grid-template-columns: repeat(4, 1fr); 
  grid-template-rows: 50px 200px 50px; 
  grid-template-areas: "head head2 head3 head4" "main main2 main3 main4" "footer footer footer footer"; 
} 

Result:

  grid-template-columns: repeat(4, 1fr); 

1fr is for 1 part of the available space, each column take up the same amount of space.

I will update the 3rd column to size up to 4fr

CSS:

.grid-container { 
  max-width: 100%; 
  margin: 3em auto; 
  display: grid; 
  grid-template-columns: 1fr 1fr 4fr 1fr; 
  grid-template-rows: 50px 200px 50px; 
  grid-template-areas: "head head2 head3 head4" "main main2 main3 main4" "footer footer footer footer"; 
} 

Result:

See a live example: CSS Grit: Fr Unit by Victor Velazquez (@vicmaster) on CodePen.

That's all folks!

Learned by Victor Velazquez on Apr 28, 2022

Behavior of Capybara's "have_no_content" when expected with `.to_not` and with `.to`

about to_not have_content:

  expect(page).to_not have_content("not on page")

it waits (Capybara's default waiting time), then it pass. Don't use it! 🚫

whereas to have_no_content:

  expect(page).to have_no_content("not on page")

Waits (same as with .to_not) but it passes as soon as "not on page" disappears from the page; use this one to wait for loading messages to disappear! ✅

Learned by victor-delarocha on Apr 21, 2022

973983 adforcodereview v3 0211021 c02 021121

Always check ACLs if something is fishy!

If you ever encounter a Linux dir in which you cannot write even when you're sure you have write permissions on it, check the ACLs. You may find someone doesn't particularly like you. 😞

-> getfacl /tmp 
# file: tmp
# owner: root
# group: root
# flags: --t
user::rwx
group::rwx
other::rwx
user:my_user:--- # this means "screw u and only u"

Learned by kevin-perez on Mar 11, 2022

973983 adforcodereview v3 0211021 c02 021121

Defining classes using full name vs wrapping them inside modules

In Ruby, we can write classes inside modules. We usually do this to have classes namespaced to the module, so we don't risk two different classes having the same name. This is important since Ruby won't complain about re-defining a class, it would just open it and add new methods to it.

We could write classes using their full name, like this:

class Pokemon::Game::SpecialAttack < Pokemon::Game::Attack
  # ...
end

or, we could wrap the whole thing using modules, like this:

module Pokemon
  module Game
    class SpecialAttack < Attack # Ruby looks for SpecialAttack inside Pokemon::Game automatically!
      # ...
    end
  end
end

Both methods work the same, except the one above can only reference Attack using its whole name. On the other hand, it looks somewhat easier to read than nesting modules inside other modules. Which style do you prefer?

Learned by kevin-perez on Mar 4, 2022

Upgrade your SSH keys from RSA to ED25519

ssh-rsa is the most common public/private key type, but is widely considered insecure with key lengths lower than 2048 bits. If you created your SSH key using ssh-keygen with default options a while ago, chances are you're using an unsafe key. Furthermore, support for RSA host keys (keys that identify the server you're trying to connect to) is disabled by default since OpenSSH 8.8 and they may consider disabling the algorithm altogether in the future.

But don't worry! Just create a new key for yourself using the most recommended key type available today: ED25519.

ssh-keygen -t ED25519 -a 100 -C "myemail@email.com"

Just make sure you got OpenSSH 6.5 or greater on both ends. Don't forget to install your new key and remove the old one!

Learned by kevin-perez on Mar 4, 2022

973983 adforcodereview v3 0211021 c02 021121

Use the spread syntax to find what an emoji is made up of

Spread syntax allows an iterable such as an array expression or string to be expanded in places where zero or more arguments (for function calls) or elements (for array literals) are expected, or an object expression to be expanded in places where zero or more key-value pairs (for object literals) are expected. [1]

[1] Source: MDN Web Docs

[...'👨‍🎤']
(3) ['👨', '', '🎤']0: "👨"1: ""2: "🎤"length: 3[[Prototype]]: Array(0)
[...'🧟']
['🧟']
[...'👩‍👩‍👧‍👧']
(7) ['👩', '', '👩', '', '👧', '', '👧']

Learned from @mgechev at https://twitter.com/mgechev/status/1490920854313648128/photo/1

Useful? not sure

Interesting? Hell yeah!

Learned by Victor Velazquez on Feb 8, 2022

973983 adforcodereview v3 0211021 c02 021121

BUILD FAILED (OS X 11.0.1 using python-build 20180424)

I had this error when I tried to install a python version with pyenv on MacOs big sur.

By running this command it worked for me!

CFLAGS=-I$(brew --prefix openssl)/include -I$(brew --prefix bzip2)/include -I$(brew --prefix readline)/include -I$(xcrun --show-sdk-path)/usr/include LDFLAGS=-L$(brew --prefix openssl)/lib -L$(brew --prefix readline)/lib -L$(brew --prefix zlib)/lib -L$(brew --prefix bzip2)/lib \ pyenv install --patch 3.8.0 < <(curl -sSL https://github.com/python/cpython/commit/8ea6353.patch\?full_index\=1)

Now when I run pyenv versions I have the python versions

Learned by samantha-bello on Jan 26, 2022

Crypt and decrypt messages with ActiveSupport::MessageEncryptor in Rails

In the Rails Console run next lines:

key = SecureRandom.base64(24) # random 32 bits key in order to encrypt
crypt = ActiveSupport::MessageEncryptor.new(key) # e.g. "PALkim1eXHeyGxFWhf+B4OvEYm6LXLtm"
encrypted_data = crypt.encrypt_and_sign('my favorite beer is La María') # "KgIkPJsn9n3JV4Y...=="
decrypted_data = crypt.decrypt_and_verify(encrypted_data) # decrypted message should concur with the original.

Learned by emmanuel-oseguera on Dec 15, 2021

973983 adforcodereview v3 0211021 c02 021121

How do you guys set up naked domains in Heroku apps?

Let's say:

I have mydomain.mx and I want to allow anybody to type in the browser

www.mydomain.mx
# or
mydomain.mx
# or
http://mydomain.mx
# or
http://www.mydomain.mx

And to resolve/redirect to the secure version of it -> https://mydomain.mx (non www)

It is basically done, following this approach:

  1. You need to go to the settings section in Heroku.com and enabling the SSL option(only available in hobby and paid plans)
  2. Add your domain with two variations inside the settings section within heroku.com

  3. Create those two CNAMEs entries in your DNS providers (I'm using cloudfare.com for free)

  4. Create a redirect rule in your application(this depend on the technology and language you are using), in my case, as I'm using Rails, so it was a matter of adding this to the top of the config/routes.rb file:

  match '(*any)',
    to: redirect(subdomain: ''),
    via: :all,
    constraints: { subdomain: 'www' }

There you go! you are ready to go, here is a live example (Site in construction as of November 26th, 2021)

www.valoralo.mx
http://valoralo.mx
http://www.valoralo.mx
valoralo.mx

All of them will resolve to the same domain: https://valoralo.mx

Learned by heriberto-perez on Nov 26, 2021

Spaceship Operator <=>

The spaceship operator compares two objects (from left to right), returning either -1, 0, or 1.

Explanation

a <=> b

return -1 if a < b
return 0 if a == b
return 1 if a > b
4 <=> 7 # -1
7 <=> 7 # 0
7 <=> 4 # 1

Example one

As you know, in ruby (as in any language) we can get a result in different ways, we could use just the sort method, of course, but I just wanted to put this in another way:

languages = ['ruby', 'go', 'javascript', 'phyton', 'rust', 'elixir']

languages.sort{|first, second| first <=> second } # ["elixir", "go", "javascript", "phyton", "ruby", "rust"]

languages.sort{|first, second| second <=> first } # ["rust", "ruby", "phyton", "javascript", "go", "elixir"]

Example two

Suppose that we have the next array with the numbers 1 to 10, and we will like to separate them into different groups: 1. One group for the numbers that are less than 5 2. Another group with the number 5 3. The last group with the numbers that are greater than 5

We could get this result by iterating the array and then by putting a couple of if statements in order to group these 3 categories, but with the spaceship operation we could get this result in an easier way:

numbers = Array(1..10)
target = 5

numbers.group_by{ |number| number <=> target } # {-1=>[1, 2, 3, 4], 0=>[5], 1=>[6, 7, 8, 9, 10]}

Learned by samantha-bello on Nov 19, 2021

973983 adforcodereview v3 0211021 c02 021121

Sort an array of objects by a property value

Imagine that we have the next object:

const names = [
  { name: 'Sam', lastName: 'Belmor'},
  { name: 'Yasser', lastName: 'Velasco' },
  { name: 'Ayrton', lastName: 'Morales' }
]

If we wanna sort those values alpabethically by name we could do this:

names.sort((a, b) => (a.name > b.name) ? 1 : -1)

We'll have this result:

[
  { name: 'Ayrton', lastName: 'Morales' },
  { name: 'Sam', lastName: 'Belmor' },
  { name: 'Yasser', lastName: 'Velasco' }
]

If return 1, the function communicates to sort() that the object b takes precedence in sorting over the object a. Returning -1 would do the opposite.

Learned by samantha-bello on Nov 16, 2021

Use 'super' and add custom arguments to a specific class

Sometimes we want to create a parent class that will be shared for children classes, nevertheless, in some of the children classes we need additional arguments that will be only specific for that class and we don't need them in the parent class, to avoid adding additional arguments that won't be relevant to the parent class, we can do the next:

class ParentClass
  attr_reader :user

  def initialize(user)
    @user = user
  end
end
class ChildOne < ParentClass
  attr_reader :token

  def initialize(user, token)
    super(user)
    @token = token
  end
end

This way the new variable token will be available only for the ChildOne class.

Learned by samantha-bello on Nov 12, 2021

973983 adforcodereview v3 0211021 c02 021121

Constant resolution operator `::`

Also, known as scope resolution operator.

When we work with namespace it's very common to override some objects, etc that we already have. This could cause us headaches when we try to access a specific object and that object has the same name as our namespace. To have a clearer idea, let's review the next example, imagine that we have the next nested modules:

module FHIR
  module Services
    module Appointment
      def appointment
        @appointment ||= Appointment.find(id)
      end
    end
  end
end

If you do Appointment.find(id) you will get an error similar to this: NoMethodError: undefined method 'find' for FHIR::Services::Appointment:Module.

That ^ is because the Appointment is doing reference to the namespace FHIR::Services::Appointment (local scope) instead of the Appointment model (global scope).

The constant resolution operator will help us to resolve this. If you put:

::Appointment.find(id)

This will work because is referencing the global namespace which is the Appointment model this way you can access directly to the model instead of the current namespace.

Learned by samantha-bello on Nov 5, 2021

'fixup' and 'rebase' for neat commits

Description

Sometimes we are working on a branch and we want a specific change in a specific commit that is not the last one, we could use fixup to have our commits neat and to avoid doing a disaster with them.

Example

Imagine that we have a commit story like this:

commit 30e9b16e098315e459f46313c099317ab74decbd
Author: Sam Belmor <sambelmor@gmail.com>
Date:  Tue Sep 28 18:57:32 2021 -0500

  Add the MVC architecture

commit e13f8600584a8b304c25d08bbaa13d1999f51569
Author: Sam Belmor <sambelmor@gmail.com>
Date:   Tue Sep 28 18:51:06 2021 -0500

  Add koa/node setup

commit b4b7ee003d554fa7eaa967bcf236c9a02c5a7249
Author: Yasser Batas <yassk8@gmail.com>
Date:   Thu Jul 15 07:11:39 2021 -0500

  Initial commit

If we do some changes related to the koa/node setup and we want those changes in the second commit e13f8600584a8b304c25d08bbaa13d1999f51569, to avoid doing another commit we could do the following:

1. Add your changes

git add .

2. Copy the commit key where you want your changes

For this example we want the changes in the commit Add koa/node setup with the key e13f8600584a8b304c25d08bbaa13d1999f51569.

git commit --fixup e13f8600584a8b304c25d08bbaa13d1999f51569

3. At this point your changes were added, if you put:

git log

You will see something like this:

commit 3ef0a9c5a3a67b5dff7a7f6374921babf7a40c12 (HEAD -> feature/#2-knex-setup)
Author: Sam Belmor <sambelmor@gmail.com>
Date:   Thu Oct 21 11:50:35 2021 -0500

  fixup! Add koa/node setup

commit 30e9b16e098315e459f46313c099317ab74decbd
Author: Sam Belmor <sambelmor@gmail.com>
Date:  Tue Sep 28 18:57:32 2021 -0500

  Add the MVC architecture

commit e13f8600584a8b304c25d08bbaa13d1999f51569
Author: Sam Belmor <sambelmor@gmail.com>
Date:   Tue Sep 28 18:51:06 2021 -0500

  Add koa/node setup

commit b4b7ee003d554fa7eaa967bcf236c9a02c5a7249
Author: Yasser Batas <yassk8@gmail.com>
Date:   Thu Jul 15 07:11:39 2021 -0500

  Initial commit

As you can see a new commit was added, with the difference that you'll see the fixup! word before the commit's description

fixup! Add koa/node setup

At this point, you should check if this is the commit where you want your changes. If this is correct go-ahead to the next point if you made a mistake you could do:

git reset HEAD~

And start again. Be sure to copy the correct commit's key.

4. Use squash to join your commits

Now you're ready to squash your new changes with your old commit. 1. First, you need to copy the previous commit's key from the one that I want to do the squash. For this example the key that I need is this key b4b7ee003d554fa7eaa967bcf236c9a02c5a7249 from this commit Initial commit 2. So you should put the following:

git rebase -i --autosquash b4b7ee003d554fa7eaa967bcf236c9a02c5a7249

5. Confirm your changes

When you do the previous command a text editor will open (nano, vim, etc), and you will see something like this:

pick e13f860 Add koa/node setup
fixup 3ef0a9c fixup! Add koa/node setup
pick 30e9b16 Add the MVC architecture

When you close that window, your changes will be saved and now you'll have the new changes in the corresponding commit.

Learned by samantha-bello on Oct 22, 2021

.forEach .map() loop JavaScript

For Each .forEach iterates the elements of an array but does not return a value.

const a = [1, 2, 3];
const newArray = a.forEach((num, index) => {
  //do something 
});

// newArray = undefined

Map .map iterates the elements of an array but return a new array.

const a = [1, 2, 3];
const newArray = a.map(num => {
  return num * 2;
});

// newArray = [2, 4, 6]

Learned by leyaim-jimenez on Oct 18, 2021

973983 adforcodereview v3 0211021 c02 021121

Separate health check endpoint using puma

Puma offers a way to query its internal stats by enabling a controll app in a separate port, this can be useful when we need to know if the app is alive, this is different than normal health check endpoints because it does not get processed by rails at all.

To enable this functionality, all you need to do is to add this line in your puma.rb file:

activate_control_app 'tcp://0.0.0.0:9293', { no_token: true }

It will start a second web server in the port 9293 that can be queried by monitoring tools or even balancer healthcheck.

ecruz@Edwins-MBP % curl 'http://127.0.0.1:9293/stats'
{"started_at":"2021-10-15T21:39:55Z","workers":2,"phase":0,"booted_workers":2,"old_workers":0,"worker_status":[{"started_at":"2021-10-15T21:39:55Z","pid":44969,"index":0,"phase":0,"booted":true,"last_checkin":"2021-10-15T21:40:05Z","last_status":{"backlog":0,"running":5,"pool_capacity":5,"max_threads":5,"requests_count":0}},{"started_at":"2021-10-15T21:39:55Z","pid":44970,"index":1,"phase":0,"booted":true,"last_checkin":"2021-10-15T21:40:05Z","last_status":{"backlog":0,"running":5,"pool_capacity":5,"max_threads":5,"requests_count":0}}]}%
ecruz@Edwins-MBP %

Check the documentation for more options/usages

Learned by Edwin Cruz on Oct 15, 2021

Numberic separators JavaScript

Numberic separators is a new JavaScript feature that allows you to use underscores as separators to help to improve readability using a visual separation between groups of digits.

// A billion
const amount = 1_000_000_000;

// Hundreds of millions     
const amount = 1_475_938.38;

// 6234500 cents (62345 dollars)
const amount = 62345_00;

// 1,734,500
const amount = 1_734_500; 

// 20^30000
const amount = 2e30_000;

Learned by leyaim-jimenez on Oct 11, 2021

973983 adforcodereview v3 0211021 c02 021121

Javascript event loop

The event loop is the secret behind JavaScript's asynchronous programming. JS executes all operations on a single thread, but using a few smart data structures, it gives us the illusion of multi-threading but first we have to understand the Call Stack.

Call Stack

The call stack works based on the LIFO principle(last in first out) When you execute a script, the JavaScript engine creates a Global Execution Context and pushes it on top of the call stack.

function multiply(a, b) {
    return a * b
}

function squere(n) {
    return multiply(n, n)
}

function printSquere(n) {
    var result = squere(n)
    console.log(result)
}

printSquere(4)

**Stack**
4- multiply
3- squere
2- printSquere
1- main

Result: 16

Call Stack with async callbacks (Even loop)

The event loop facilitates this process; it constantly checks whether or not the call stack is empty. If it is empty, new functions are added from the event queue. If it is not, then the current function call is processed.

An example of this is the setTimeout method. When a setTimeout operation is processed in the stack, it is sent to the corresponding API which waits till the specified time to send this operation back in for processing.

console.log('hi')

setTimeout(()=> {
  console.log('there')
},5000)

console.log('bye')

**Stack**
3- there
2- bye
1- hi

**webapis**
setTimeout callback(cb)

**taskqueue**
callback(cb)

**evenloop**
move cb to stack

In the example the setTimeout Callback enters the webapi stack executes and then goes to the task queue and waits for the stack to become empty and the event loop moves the callback to the stack.

Learned by leyaim-jimenez on Oct 1, 2021

module_function

What it does?

  • module_function allows exposing instance’s methods so they can be called as they would be class methods.
module User
  def name
    'Hello Sam'
  end
end

If you try to do this:

user = User.new
user.name

You're gonna receive an error because modules do not respond to the new method.

How can we use it?

You can use this useful method module_function:

module User
  module_function

  def name
    'Hello Sam'
  end
end

And call the name method like User.name

  1. Use module_function to use all the methods inside a module as class methods or
  2. Use module_function :name to only apply it in a specific method

A second option to do it

Another option to do so is using extend self instead:

module User
  extend self

  def name
    'Hello Sam'
  end
end

Learned by samantha-bello on Sep 29, 2021

973983 adforcodereview v3 0211021 c02 021121

Truthy and Falsy Values

If JavaScript's built-in type coercion makes it true, thats mean that the values is a truthy.

  true
  {}
  []
  42
  "0"
  "false"
  new Date()
  -42
  12n
  3.14
  Infinity
  -Infinity

and in Javascript a falsy value is a value that is considered false

  false
  0
  0n: 0 as a BigInt
  '': Empty string
  null
  undefined
  NaN

Learned by leyaim-jimenez on Sep 28, 2021

Underscores are not allowed as part of domain names

According to rfc1035, underscores (_) are not allowed as part of domain names.

The labels must follow the rules for ARPANET host names. They must start with a letter, end with a letter or digit, and have as interior characters only letters, digits, and hyphen (-). There are also some restrictions on the length. Labels must be 63 characters or less.

This means domain names like my_domain.com or sub_domain.main-domain.com are invalid.

Special thanks to @jclopezdev for finding this out.

Learned by kevin-perez on Sep 28, 2021

973983 adforcodereview v3 0211021 c02 021121

JavaScript null vs undefined

In JavaScript null and undefined are rather strange values, both serve a very similar purpose, which is to indicate the absence of a value.

Null

Null is used to assign a reference to an object that you will no longer need or, directly, you want to have the variable declared but initialize it with a value that you still do not know what it will be exactly. In all these cases the best thing to do is to assign a null value.

  var miVariable = null;
  console.log(miVariable);

//log null

undefined

For undefined means that the variable is declared but its value has not yet been defined.

  var miVariable
  console.log(miVariable);

//log null

Both values are values of type false, so if you do a non-strict comparison you will get true undefined, which means that the variable is declared but its value has not yet been defined.

if (null == undefined) {
  return true
 }
//log true

and if you do a strict comparison, because they are not really the same, it returns a false:

if (null === undefined) {
   return true
 }
return false
//log false

Learned by leyaim-jimenez on Sep 21, 2021

Javascript Hoisting

This means that variable and function declarations are physically moved to the beginning of the code and are allocated in memory during the compilation phase.

function welcomeTo(name) {
  console.log("Welcome to " + name);
}

welcomeTo("Magmalabs");
//returns welcome to magmalabs

As you can see hoisting allows you to use a function before declaring it in the code.

welcomeTo("Magmalabs");

function welcomeTo(name) {
  console.log("Welcome to " + name);
}
//returns welcome to magmalabs

And also allows you to use a function after declaring it in the code.

For variables, hoisting only applies to the declaration, not to their assignment. example:

We declare the variable name but we got an undefined

console.log(name); 
var name = magmalabs;
//return undefined

That’s because JavaScript only hoist the declaration

var name;

console.log(name); 
name = magmalabs;
//return undefined

To avoid these errors, it is very important to keep in mind that hoisting only applies to the declaration.

Learned by leyaim-jimenez on Sep 17, 2021

973983 adforcodereview v3 0211021 c02 021121

Profiles in docker compose, specify default services

It is common to have multiple services listed in the docker-compose file, sometimes with dependencies, but also, a normal workflow sometimes is to just start everything, like:

docker-compose -f docker-compose.yml -d

And it will start all servies listed with their dependencies, but, what happens if you need to add another service and you do not need it to start along with the rest? The answer is: profiles:

services:
  db:
    image: pg
  cache:
    image: redis
  rails:
    depends_on:
      - db
      - cache
    command: puma
  sidekiq:
    profiles: [ 'jobs' ]
    depends_on:
      - db
      - cache
    command: sidekiq

So, in this case, whenever you do docker-compose up -d it will start only rails with its dependencies: db and cache, it wont start sidekiq by default, but if you really want to start sidekiq, then you need to explicitly type it: docker-compose up sidekiq -d.

Learned by Edwin Cruz on Sep 15, 2021

Eager load rails associations with nested scopes

It is common to apply some extra scopes when fetching AR relationships, for examples, if we have countries and states, we might want all the countries starting with the letter A and all their states that starts with the letter B, this will automatically create a n+1 query problem since it nees to iterate over each country and fetch all states, but, Rails provides a way to eager load these associations easily:

states_scope = State.where("name ilike 'b%'")
countries = Country.where("name ilike 'a%'")
# This is the magic
ActiveRecord::Associations::Preloader.new.preload(countries, :states, states_scope)

# Now you can invoke coutries.each(:states) and it wont cause queries N+1
countries.map {|country| { country.id => country.states.size }

Normally, you would have to define another relationship in order to eager load the association, but it is not needed using this approach:

class Country < AR::Base
  has_many :states
  has_many :states_starting_with_b, -> { where("name ilike 'b%'") }, foreign_key: :state_id, class_name: "State"
end

# Then
Country.includes(:states_starting_with_b).where("name ilike 'a%'")

But this approach does not scale, it requires to define tons of relationships

Learned by Edwin Cruz on Sep 9, 2021

973983 adforcodereview v3 0211021 c02 021121

Docker compose services with health checks

If you need to add extra checks for services to be up and running before starting another one, you can use healtcheck property:

services:
  pg:
    image: pg/pg-12
    ports:
      - 5432:5432
    healtcheck:
      test: [ "CMD-SHELL", "pg_isready -U postgres" ]
      interval: 5s
      timeout: 5s
      retries: 5
  redis:
    image: redis:5.0.4-alpine
    ports:
      - 6380:6379
    healthcheck:
      test: [ "CMD", "redis-cli", "ping" ]
      interval: 5s
      timeout: 5s
      retries: 5
  app:
    image: railsapp
    depends_on:
      pg:
        condition: service_healthy
      redis:
        condition: service_healthy
    ports: ['3000:3000']
    command: |
      bash -c "bundle exec rails s"

Learned by Edwin Cruz on Sep 8, 2021

Typeprof Ruby Interpreter in Ruby 3.0+

TypeProf is a Ruby interpreter that abstractly executes Ruby programs at the type level. It executes a given program and observes what types are passed to and returned from methods and what types are assigned to instance variables. All values are, in principle, abstracted to the class to which the object belongs, not the object itself.

Example: 1

 $ typeprof user.rb
# TypeProf 0.12.0

# Classes
class User
  attr_accessor skip_add_role: untyped
  def self.from_omniauth: (?Hash[bot, bot] auth) -> User

  private
  def assign_default_role: -> nil
end

Example 2:

 $ typeprof skill.rb
# TypeProf 0.12.0

# Classes
class Skill
  private
  def acceptable_image: -> nil
end

Example 3:

 $ typeprof ability.rb
# TypeProf 0.12.0

# Classes
class Ability
  attr_accessor user: untyped
  def initialize: (untyped user) -> untyped

  private
  def alias_actions: -> untyped
  def register_extension_abilities: -> untyped
end

Learned by Victor Velazquez on Aug 13, 2021

973983 adforcodereview v3 0211021 c02 021121

Rails + Facebook Oauth Locally with SSL

Recently I had the need to test Oauth with Facebook locally and after creating and configuring the App and everything was working wonderfully ...

until it was not.

Facebook now forces SSL so I had to setup it locally by creating a self signed certificate and running my server with it.

  1. Create your certificate, this script create it as localhost.mumoc.crt and localhost.mumoc.key. Mumoc is my username in my working machine.
name=localhost.$(whoami)
openssl req \
  -new \
  -newkey rsa:2048 \
  -sha256 \
  -days 3650 \
  -nodes \
  -x509 \
  -keyout $name.key \
  -out $name.crt \
  -config <(cat <<-EOF
  [req]
  distinguished_name = req_distinguished_name
  x509_extensions = v3_req
  prompt = no
  [req_distinguished_name]
  CN = $name
  [v3_req]
  keyUsage = nonRepudiation, digitalSignature, keyEncipherment
  extendedKeyUsage = serverAuth
  subjectAltName = @alt_names
  [alt_names]
  DNS.1 = $name
  DNS.2 = *.$name
EOF
)

Make sure to at least add digitalSignature and keyEncipherment to KeyUsage or you won't be able to use it in Chrome

  1. Trust the certificate (I moved it to a config/ssl directory inside my app folder)
mv localhost.mumoc.* config/ssl
sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain config/ssl/localhost.mumoc.crt
  1. Run server binding the ssl with the key and certificate
rails s -b 'ssl://localhost:3000?key=config/ssl/localhost.mumoc.key&cert=config/ssl/localhost.mumoc.crt'

Learned by carlos-muniz on Jul 29, 2021

973983 adforcodereview v3 0211021 c02 021121

ON DUPLICATE KEY UPDATE

When you're doing an INSERT query, you could be trying to insert a row containing a primary key that already exists in the table. Instead of doing a previous query to see if the key exists or not, you could try ON DUPLICATE KEY UPDATE.

INSERT INTO table (
  field1, 
  field2
) 
VALUES (
  "foo", 
  "bar"
) 
ON DUPLICATE KEY UPDATE 
  field1="foo", 
  field2="bar"

Learned by kevin-perez on Jul 6, 2021

You can forward/reverse ports on Android device using adb

I'm developing an Android app as a side project and today I learned about adb forward and adb reverse. Basically, they allow you to forward or reverse traffic to specific ports between an Android device and your development machine.

Let's say I need my app to fetch something from http://localhost:3000/some-data. When the app is running on the phone localhost refers to the phone itself, and my server is running in my dev machine. So, if I do this:

adb reverse tcp:3000 tcp:3000

Now when the app tries to access localhost:3000 it will actually reach out to my dev machine's 3000 port. Very useful if you're developing the app's backend as well (as I am).

Similarly, if I wanted to access a server inside the phone from my dev machine, I could run:

adb forward tcp:5000 tcp:5000

And now if I run curl http://localhost:5000 in my dev machine it will hit the server running on the phone. Pretty neat!

Learned by kevin-perez on Jun 30, 2021

973983 adforcodereview v3 0211021 c02 021121

How to run a single test from a file using Cypress

I can run all tests from a file with:

yarn run cypress:run -s path/to/file.spec.js

but what about when I want to run only one test case from that file and not all of them?

We could use only()

// path/to/file.spec.js

it.only('just run this test', () => { ... })

it('not run this test', () => { ... })

// we could also use describe.only() but IDK if that is a bug or a feature xD

Run again yarn run cypress:run -s path/to/file.spec.js to see how the it.only() test will run it

Learned by rogelio-alatorre on Jun 25, 2021

TIL about the attention economy

As a UX designer, the product you create will constantly compete for users’ attention. If users are paying attention to one thing, like a notification on their mobile phone, it means they’re ignoring something else, so users have to be selective about how they spend time! This battle over users’ attention is referred to as the attention economy.

The term attention economy originated with psychologist and Nobel Laureate Herbert A. Simon, who believed that there are limits on what humans can think about and do at one time. Many scientists believe that humans aren’t very good multitaskers; technology should help users, not distract them. The more distracted a person is, the less likely they are to complete a task well.

Learned by aldo-mosqueda on Jun 14, 2021

973983 adforcodereview v3 0211021 c02 021121

Set and preserve cookies on and Axios API call

I was using Axios to test an API created in Rails, trying to set up cookies for a User Authentication process. After attempting to get the information back i realize that the cookie was not persistent, due to the lack of one parameter... withCredentials: true So if you want your session to store cookies in the client side and have them available remember to pass it to the call import axios from import axios from 'axios' axios.post(API_SERVER + '/login', { email, password }, { withCredentials: true }) Otherwise the cookie would not be saved.

Learned by fabian-alcala on Jun 10, 2021

The Mehrabian rule

Today I learn the Mehrabian 7-38-55 rule. The rule states that: 7% of meaning is communicated through spoken word 38% through tone of voice, and 55 % through body language.

It was developed by psychology professor Albert Mehrabian at the University of California, Los Angeles.

So... it is always a good idea to keep our cameras on at every meeting ;)

Learned by aldo-mosqueda on May 28, 2021

Other errors when installing Capybara Webkit Gem - M1 Mac Big Sur

Well, it turns out that installing capybara-webkit gem can give you different problems, in my case I had to install

capybara-webkit -v '1.15.1'.

You need to install QT as a dependecy.

The latest versions of QT including 5.6 returns the following message:

QtWebKit is no longer included with Qt 5.6

I followed one of our guides here, however it appears that QT version 5.5 was removed.

cd $( brew --prefix )/Homebrew/Library/Taps/homebrew/homebrew-core
git checkout 9ba3d6ef8891e5c15dbdc9333f857b13711d4e97 Formula/qt@5.5.rb
brew install qt@5.5

curl: (22) The requested URL returned error: 404
Error: Failed to download resource "qt@5.5_bottle_manifest"
Download failed: https://ghcr.io/v2/homebrew/core/qt/5.5/manifests/5.5.1_1

I also tried through binary installer but I got the following error:

Error creating SSL context

I finally succeeded, installing an older version

brew tap cartr/qt4
brew install qt@4
brew install qt-webkit@2.3
gem install capybara-webkit -v '1.15.1'

Building native extensions. This could take a while...
Successfully installed capybara-webkit-1.15.1
1 gem installed

Learned by jennifer-ledezma on May 26, 2021

973983 adforcodereview v3 0211021 c02 021121

while on a Capybara / Cucumber test execution How to save and open the page as snapshot in a browser for inspection

While debugging a cucumber test (within an specific step for an specific scenario) with a binding.pry I wanted to see the html as it is with inputs and outputs of data at the moment (and because I usually run tests with Capybara headless option), I realized the existance of:

save_and_open_page

it Saves a snapshot of the page and open it in a browser for inspection

https://www.rubydoc.info/github/jnicklas/capybara/Capybara%2FSession:save_and_open_page

Note: ^ be sure to have the gem 'launchy' within test env so that it works

Learned by victor-delarocha on May 12, 2021

Fixing the Capybara Webkit Gem installation - QtWebKit is no longer included with Qt 5.6 error on M1 Mac with Big Sur

If you installed any version above 5.6 make sure you uninstall all those versions, for instance:

MacBook-Air:myproject heridev$ brew uninstall qt
Uninstalling /usr/local/Cellar/qt/6.0.3_2... (8,233 files, 158.7MB)
MacBook-Air:myproject heridev$ brew uninstall qt@5
Uninstalling /usr/local/Cellar/qt@5/5.15.2... (10,688 files, 367.9MB)

Then we need to install the 5.5 version (old version):

cd $( brew --prefix )/Homebrew/Library/Taps/homebrew/homebrew-core
git checkout 9ba3d6ef8891e5c15dbdc9333f857b13711d4e97 Formula/qt@5.5.rb
// this will install the qt from shopify/shopify
brew install qt@5.5

And make sure you add the qt PATH in my case:

echo 'export PATH="/usr/local/opt/qt@5.5/bin:$PATH"' >> /Users/heridev/.bash_profile
source ~/.bash_profile

Now, you should be able to install the capybara webkit gem without any problems

Learned by heriberto-perez on May 5, 2021

973983 adforcodereview v3 0211021 c02 021121

User Stories

As a [UX Designer] I want to [embrace Agile] so that [I can make my projects user-centered]

TIL that User Stories were originated as part of the Agile and SCRUM development methodologies. A tool so simple yet so powerful, it is a great design method that enhances collaboration.

Read the entire article: https://bit.ly/3tkB3RE

Learned by aldo-mosqueda on May 5, 2021

Control email deliveries in Rails with a custom Interceptor

You can easily tell Rails to control email delivering with a custom mailer interceptor, all you need to do is to implement the class method delivering_email:

class DeliverOrNotEmailInterceptor
  def self.delivering_email(email)
    mail.perform_deliveries = !email.to.end_with?('special-domain.com')
  end
end

# config/initializer/email_interceptors.rb
ActionMailer::Base.register_interceptor(DeliverOrNotEmailInterceptor)

Learned by Edwin Cruz on May 4, 2021

973983 adforcodereview v3 0211021 c02 021121

Safe access for nested key values in a Hash - dig method

If you are using Ruby 2.3.0 or above

Now instead of doing this:

result.try(:[], 'avatar').try(:[], 'model').try(:[], 'raw_attributes').try(:[], 'signup_state')
# or
result && result['avatar'] && result['avatar']['model'] && result['avatar']['model']['raw_attributes'] && result['avatar']['model']['raw_attributes']['signup_state']

Now you can easily do the same with dig:

result.dig('avatar', 'model', 'raw_attributes', 'signup_state')

Learned by heriberto-perez on Apr 28, 2021

973983 adforcodereview v3 0211021 c02 021121

How to add an image to a gist

1.- Create a gist

2.- Clone your gist:

# make sure to replace `<hash>` with your gist's hash
git clone https://gist.github.com/<hash>.git # with https
git clone git@gist.github.com:<hash>.git     # or with ssh

3.- Open it & move your image

cd <hash>
mv ~/Desktop/image.jpg ~/Projects/gist/image.jpg

4.- Add your image to your gist's repository:

git add image.jpg

5.- Commit the image:

git commit -m "Add image"

6.- Update gist:

git push origin master

image

Learned by Victor Velazquez on Apr 22, 2021

How to understand the SQL behind an ActiveRecord Query

Quick example, to see and understand better how an ActiveRecord query using scopes or methods in the model works at the SQL level and how to run it, all in the same example

Ruby version

Let say you have a database with users and you have some scopes to filter by non-demo, active, and completed providers with a specific Role (CanCanCan behind the scenes) :

User.providers.real.active.completed

Let's look into the SQL version:

If we run this command

irb(main):007:0> User.providers.real.active.completed.to_sql

We would get this:

=> "SELECT \"users\".* FROM \"users\" INNER JOIN \"users_roles\" ON \"users_roles\".\"user_id\" = \"users\".\"id\" INNER JOIN \"roles\" ON \"roles\".\"id\" = \"users_roles\".\"role_id\" WHERE (roles.name = 'Provider') AND \"users\".\"demo_account\" = 'f' AND (\"users\".\"organization_id\" NOT IN (SELECT \"organizations\".\"id\" FROM \"organizations\" WHERE \"organizations\".\"demo_account\" = 't' ORDER BY \"organizations\".\"name\" ASC)) AND \"users\".\"is_active\" = 't' AND \"users\".\"signup_state\" = 'Completed'"

Then we just need to replace the invalid characters such as \ and translate that into a SQL version like this:

SUMMARY_PROVIDERS_REPORT_SQL = <<-SQL
WITH real_providers AS
  ( SELECT users.* FROM users
    INNER JOIN users_roles ON users_roles.user_id = users.id
    INNER JOIN roles ON roles.id = users_roles.role_id
    WHERE (roles.name = 'Provider') AND users.demo_account = 'f'
      AND (users.organization_id NOT IN (
        SELECT organizations.id FROM organizations WHERE organizations.demo_account = 't' ORDER BY organizations.name ASC)
      )
      AND users.is_active = 't' AND users.signup_state = 'Completed')
select real_providers.id, real_providers.name from real_providers
SQL

In order to run it in the Rails console for example:

report_results = ActiveRecord::Base.connection.execute(SUMMARY_PROVIDERS_REPORT_SQL)
report_results.entries

That will give you the id and name of all those valid providers

Learned by heriberto-perez on Apr 21, 2021

973983 adforcodereview v3 0211021 c02 021121

:is() pseudo-class - CSS

When we apply the same style to multiple selectors on CSS we use to do something like this:

.section-header h1,
.section-header span,
.section-header .heading {
  line-height: 1.2;
}
.navigation li,
.navigation p {
  padding: 5px 10px;
}

With :is() we can write our CSS in a shorter way.

.section-header :is(h1, span, .heading) {
  line-height: 1.2;
}
.navigation :is(li, p) {
  padding: 5px 10px;
}

Learned by carlos-pineda on Apr 19, 2021

973983 adforcodereview v3 0211021 c02 021121

Notes on getting my M1 Rails + Foreman environment working

I expect this to evolve quickly and different things seem to be working for different people. In the meantime, hope to avoid a few rabbit holes for others.

  1. rvm worked for ruby 3.0.0 but not 2.6.6 (which is what I needed). cflags workarounds made it so ruby could get installed but gems with native extensions like grpc could not compile correctly. Tried asdf before trying rbenv and it worked, so I stuck with asdf.
  2. Had to prepend every brew installation (including brew itself) with arch -x86_64 brew install ....
  3. After installing asdf, I installed bundler as usual. No arch -x86_64 or anything. Same for bundle install afterwards.
  4. This DriftingRuby episode helped a lot.

I don't think the following were necessary but this is what I did because installing things with brew was getting hairy:

  1. Use Postgres.app to keep PostgreSQL running in the background, deleted it from my Procfile. Naturally, I have to start Postgres manually before I start Foreman.
  2. Downloaded ElasticSearch binaries and put the path in my Procfile. Essentially /Users/jose/dependencies/elasticsearch/elasticsearch-#.#.#/bin/elasticsearch

Learned by jose-farias on Apr 9, 2021

973983 adforcodereview v3 0211021 c02 021121