35 posts about #ruby

Mastering extend and when you want class methods and instance methods for your class

Mastering extend and when you want class methods and instance methods for your class

I just realized something cool, which is the underlying of including same module for a class and share instance and class methods with that class.

So here is the trick

You have this simple module:

module TestModule
  def test_name
    'test_name'
  end
end

And this class

class Test
  def name
    'Test'
  end
end
Test.extend(TestModule)
irb(main):033> Test.test_name
=> "test_name"
irb(main):034> Test.new.name
=> "Test"

So that is why if you want to include both instance and class methods, with something like this

class Test
  include TestModule
end

How would you include some as class methods, and the answer is by defining those methods in a module or subgroup within the module, and it would look like this

module TestModule
  def instance_method
    'instance method'
  end

  module TheClassMethods
    def class_hello
      'class_hello'
    end
  end

  def self.included(base)
    base.extend(TheClassMethods)
  end
end

class Test
  include TestModule
end

So now you can access both of them

Test.new.instance_method # instance method
Test.class_hello # class_hello

NOTE: remember that it is included in the past

def self.included(base)
  base.extend(YouClassMethodsToShare)
end

And active_support/concern comparison

module M
  def self.included(klass)
    klass.extend ClassMethods
    klass.class_eval do
      scope :disabled, -> { where(disabled: true) }
    end
  end

  def instance_method
    'instance method'
  end

  module ClassMethods do
     def class_method_one
       'class method'
     end
  end
end

Vs

require 'active_support/concern'

module M
  extend ActiveSupport::Concern

  included do
    scope :disabled, -> { where(disabled: true) }

    def self.direct_class_method_here
      ..
    end
  end

class_methods do
    def class_method_one
      'class method one'
    end
  end
end

class YourClass
  include M
end

What is the difference between "and" and "&&" in Ruby or between "||" and "or"?

First of all, all of them are logical operators, but the assignment operator = has higher precedence than and and or, but lower precedence than && and ||.

And if not understood correctly it can lead to tricky and unexpected results, for instance:

irb(main):064* def execute_notification
irb(main):065*   puts 'execute_notification'
irb(main):066> end
=> :execute_notification
irb(main):067> execute_notification
execute_notification
=> nil
irb(main):068> result = true and execute_notification
execute_notification
=> nil
irb(main):069> result
=> true
irb(main):070> result = false and execute_notification
=> false
irb(main):071> result
=> false
irb(main):072> result = true or execute_notification
=> true
irb(main):073> result = false or execute_notification
execute_notification
=> nil
irb(main):074> result
=> false
irb(main):075> result = true && execute_notification
execute_notification
=> nil
irb(main):076> result
=> nil
irb(main):077> result = false && execute_notification
=> false
irb(main):078> result
=> false
irb(main):079> result = true || execute_notification
=> true
irb(main):080> result
=> true
irb(main):081> result = false || execute_notification
execute_notification
=> nil
irb(main):082> result
=> nil

Exponenciation in Ruby <> Javascript and Ruby to_i in Javascript and prevent rounding float/decimals

Let’s begin with an actual example and conversion for both languages(Ruby and JavaScript).

Most of the time, if you want to take n number of decimals after the . from a big decimal or just fill it with zeros, you would do something like this:

Ruby

number = 4.9999999
# eg: with a precision of 6
'%.6f' % number # => "5.000000"
sprintf('%.6f', number) # => "5.000000"
format('%.6f', number) # => "5.000000"

Javascript

number = 4.9999999
number.toFixed(6) => '5.000000'

How to solve that problem?

By using a custom method!

Ruby

def truncate_float(number, precision)
  factor = 10 ** precision
  (factor * number).to_i / factor.to_f
end

result = truncate_float(4.9, 6)# => 4.9
# then
'%.6f' %  result # => "4.900000"
truncated_value = truncate_float(4.9999999, 6)  # => 4.999999
'%.6f' % truncated_value # => "4.999999"

Now in JavaScript

function truncateDecimal(decimalNumber, precision) {
  factor = Math.pow(10, precision)
  return Math.floor(factor * decimalNumber) / factor
}

truncateDecimal(4.9, 6) // 4.9
truncateDecimal(4.9999999999, 6) // 4.999999

There you have it!

Verify existence of arbitrary email addresses from the command line

#!/usr/bin/env ruby
# frozen_string_literal: true

require 'resolv'
require 'net/smtp'

def mx_records(domain)
  Resolv::DNS.open do |dns|
    dns.getresources(domain, Resolv::DNS::Resource::IN::MX)
  end
end

def mailbox_exist?(email)
  domain = email.split('@').last
  mx = mx_records(domain).first
  return false unless mx

  Net::SMTP.start(mx.exchange.to_s, 25) do |smtp|
    smtp.mailfrom 'info@example.com' # replace with your email address or something more realistic
    smtp.rcptto email
  end
  true
rescue Net::SMTPFatalError, Net::SMTPSyntaxError
  false
end

if ARGV.length != 1
  puts "Usage: ruby #{__FILE__} <email_address>"
  exit 1
end

email = ARGV[0]
if mailbox_exist?(email)
  puts "Mailbox exists."
else
  puts "Mailbox doesn't exist or couldn't be verified."
end

Ruby partition on arrays

In Ruby, the partition is a very useful method that you can use to filter some items in an array and that you need the ones that satisfy the condition and the ones that do not, and it takes a block of code and returns two arrays: the first contains the elements for which the block of code returns true, and the second contains the elements for which the block returns false.

Let's see an actual example:

def create_fake_emails_array
  emails = []
  10.times do |i|
    emails << { email: "user#{i + 1}@mailinator.com" }
  end

  10.times do |i|
    emails << { email: "user#{i + 1}@something.com" }
  end
  emails
end

my_emails = create_fake_emails_array

class EmailContactsWhitelistCleaner
  attr_reader :email_recipients

  def initialize(email_recipients)
    @email_recipients = email_recipients
  end

  def get_white_list_collection
    valid_recipients, invalid_recipients = partition_emails

    log_black_list_email_recipients(invalid_recipients)
    valid_recipients
  end

  private

  def partition_emails
    email_recipients.partition { |recipient| valid_recipient?(recipient[:email]) }
  end

  def valid_recipient?(email)
    !email.match?('mailinator') || mailinator_white_list.include?(email)
  end

  def log_black_list_email_recipients(invalid_recipients)
    return if invalid_recipients.empty?

    email_list = invalid_recipients.map { |recipient| recipient[:email] }.join(',')
    puts "The following emails are not in the whitelist: #{email_list}"
  end

  def mailinator_white_list
    # ENV.fetch('MAILINATOR_WHITE_LIST', '').split(',')
    'user1@mailinator.com,user2@mailinator.com,user3@mailinator.com'
  end
end

service = EmailContactsWhitelistCleaner.new(my_emails)
puts service.get_white_list_collection

How to make Wisper properly load all subscribers in the app/subscribers directory structure

Rails.application.reloader.to_prepare do
  # Dev env will re-install subscribers on app reload
  Wisper.clear if Rails.env.development?

  Dir[Rails.root.join("app", "subscribers", "**", "*.rb")].each do |listener|
    relative_path = listener.gsub("#{Rails.root}/app/subscribers/", "")
    klass = relative_path.chomp(".rb").camelize.safe_constantize
    Wisper.subscribe(klass.new, async: !Rails.env.test?)
  end
end

Sharing since the sample provided by the project itself won't work with namespaced subscribers.

Fix Psych loading errors on wisper-sidekiq

One of my favorite rubygems is Wisper, a simple library that lets you add pub/sub style broadcasting and listeners to your app. (Been a fan since it came out in 2014, almost ten years ago!)

I tried to use wisper again recently, specifically with the wisper-sidekiq companion gem, which allows your subscribers to execute asynchronously as Sidekiq jobs.

Unfortunately, I immediately ran into an issue with Psych complaining about my parameters (ActiveRecord model instances) not being allowed by the YAML loading subsystem. If you're running into this same kind of issue, you'll know because you get exceptions that look like Tried to load unspecified class: Account (Psych::DisallowedClass)

Sidestepping the question of whether you should stick to only sending primitive object parameters (strings, integers, etc) as arguments to Sidekiq jobs, here is the monkeypatch solution to solving the problem, tested with wisper-sidekiq version 1.3.0.

Chuck this override into an initializer file.

module Wisper
  class SidekiqBroadcaster
    class Worker
      include ::Sidekiq::Worker

      def perform(yml)
        (subscriber, event, args) = ::YAML.unsafe_load(yml)
        subscriber.public_send(event, *args)
      end
    end
  end
end

The fix is the replacement of YAML.load with YAML.unsafe_load on line 16.

But Obie, isn't this dangerous? No. Objects passed in broadcast events are almost certainly not coming from the outside world in any way, shape, or form, so the reasons that you would typically be interested in blocking YAML loading from processing arbitrary objects do not apply.

But Obie, if your queues back up, won't you have stale data sitting in Redis as parameters to your jobs? For my particular use case this is not a concern, but for yours it might be. Either way it's not the job of the sidekiq-wisper library to enforce this constraint... and indeed, since the library predates the safety additions to YAML.load I'm not even sure that the author intended for the constraint to exist.

Now what would really be cool, and I wish I had time to implement this myself, is if sidekiq-wisper would automatically turn activerecord params into globalid identifiers and query them for you on the consumer side, the way that sidekiq does. Somebody should definitely implement something like that!

Extracting JSON Code with Nested Curly Braces in Ruby (the long painful way around, with help from GPT4)

Given a text string that contains JSON code with possible nested curly braces, I needed to extract the outermost JSON code, including the curly braces. Here's an example of such text, which you may recognize as the output of an LLM (specifically GPT in this case):

Here's the JSON you requested:

{
 "title": "Brainstorming ideas",
 "summary": "The user discussed exporting basic profile bots",
 "sentiment": "positive",
 "language": "English",
 "additional_information": {
    "tags": ["brainstorming", "bots", "automation"]
  }
 }

An initial crack at extracting just the JSON with a regex might look like this, but the outermost selector is too greedy.

> text.match /.*(\{.*\})/m; $1
=> "{\n    \"tags\": [\"brainstorming\", \"bots\", \"automation\"]\n  }\n }"

I took a hard look at that regex and figured that I just needed to make the first .* less greedy for it to work fine, like this:

/.*?(\{.*\})/m

And here is where the story gets more interesting (or stupid, depending on your perspective.) Because I tried it and unfortunately it DID NOT work. And since it was late at night and I was tired, instead of trying to figure out why I got frustrated and asked GPT4 to help me out.

GPT offered me this regex instead: /(?<=\{)(?s:.*)(?=\})/m

I've occasionally had to use PERL over the years so I was at least passingly familiar with the syntax here. It uses a positive lookbehind (?<=\{) to assert that the match starts after an opening curly brace, and a positive lookahead (?=\}) to assert that the match ends before a closing curly brace. The (?s:.*) part matches any characters, including newlines, between the curly braces, and finally, the /m flag is used for multi-line matching. Which should have worked fine, except that I'm coding in Ruby, so it generated this exception: undefined group option

It turns out that Ruby's regex engine does not support these operations (or if it does, I was not able to figure it out).

When I pointed this out to GPT4 it proposed a more straightforward and brute-force approach by writing a Ruby method that loops through the text and counts the opening and closing curly braces. This method can efficiently find the starting and ending indices of the outermost JSON object.

Here's the Ruby code to extract the JSON code from the text:

def extract_json(text)
  start_index = text.index('{')
  return nil if start_index.nil?

  end_index = start_index
  brace_count = 1

  text[start_index + 1..-1].each_char.with_index do |char, index|
    brace_count += 1 if char == '{'
    brace_count -= 1 if char == '}'
    end_index += 1

    break if brace_count.zero?
  end

  text[start_index..end_index]
end

This code defines a method called extract_json that takes the input text as an argument. It then loops through the text to find the starting and ending indices of the outermost JSON object by counting the opening and closing curly braces. Once the outermost JSON object is found, it is returned as a string.

It passed my tests so I committed the code and called it a night. But that first failed regex solution still bothered me this morning, and eventually I opened a terminal and started playing around with it again. Making that first .* operator less greedy should work.

And guess what? It does work.

> text
=> "Here is your JSON:\n\n```\n{\n \"title\": \"Brainstorming ideas\",\n \"summary\": \"The user discussed exporting basic profile bots\",\n \"sentiment\": \"positive\",\n \"language\": \"English\",\n \"additional_information\": {\n    \"tags\": [\"brainstorming\", \"bots\", \"automation\"]\n  }\n }\n```\n"

> text.match /.*?(\{.*\})/m; $1
=> "{\n \"title\": \"Brainstorming ideas\",\n \"summary\": \"The user discussed exporting basic profile bots\",\n \"sentiment\": \"positive\",\n \"language\": \"English\",\n \"additional_information\": {\n    \"tags\": [\"brainstorming\", \"bots\", \"automation\"]\n  }\n }"

Why I couldn't get it to work yesterday? No idea. But I did learn a lesson about trusting GPT4 over my own instinct. I'm going to call it a double-edged sword. You got to be careful you don't cut yourself. This particular solution wasn't that big a deal, but I can imagine situations where it would be.

Interestingly enough, I pointed out the failure to GPT4 and it admitted that it made a mistake.

You are correct, and I apologize for my earlier explanation. The regex /.*?(\{.*\})/m indeed works for extracting the outermost JSON code, including when there are nested curly braces, as demonstrated in your example. The non-greedy .*? before the capturing group ensures that the regex starts matching at the first opening curly brace. The greedy .* inside the capturing group then matches all characters, including nested curly braces, until the last closing curly brace is found.

Thank you for pointing this out, and I apologize for the confusion in my earlier responses.

Using OpenAI's TikToken in Ruby

The encoding used in the tiktoken library (and the Ruby binding discussed in this post) is a specific way of converting text into a sequence of tokens, which are then represented by their unique IDs. The encoding scheme is designed to work with OpenAI models like gpt-3.5-turbo and is based on the model's vocabulary and tokenizer.

There's a simple Ruby binding for TikToken made by Iapark that compiles the underlying Rust library. https://rubygems.org/gems/tiktoken_ruby

First add it to your Gemfile

gem "tiktoken_ruby"

Then use it in your code. The service module I wrote today to use it in my Rails app looks like this:

require 'tiktoken_ruby'

module TikToken
  extend self

  DEFAULT_MODEL = "gpt-3.5-turbo"

  def count(string, model: DEFAULT_MODEL)
    get_tokens(string, model: model).length
  end

  def get_tokens(string, model: DEFAULT_MODEL)
    encoding = Tiktoken.encoding_for_model(model)
    tokens = encoding.encode(string)
    tokens.map do |token|
      [token, encoding.decode([token])]
    end.to_h
  end
end

Here's what it looks like in practice.

irb> TikToken.count("Absence is to love what wind is to fire; it extinguishes the small, it inflames the great.")
=> 19

irb> TikToken.get_tokens("Absence is to love what wind is to fire; it extinguishes the small, it inflames the great.")
=>
{28878=>"Abs",
 768=>"ence",
 374=>" is",
 311=>" to",
 3021=>" love",
 1148=>" what",
 10160=>" wind",
 4027=>" fire",
 26=>";",
 433=>" it",
 56807=>" extingu",
 21168=>"ishes",
 279=>" the",
 2678=>" small",
 11=>",",
 4704=>" infl",
 986=>"ames",
 2294=>" great",
 13=>"."}

The encoding is essential for processing text with the OpenAI models, as it allows them to understand and generate text in a format that is compatible with their internal representations. In the context of the tiktoken library, the encoding is particularly helpful for estimating token counts in a text string without making an API call to OpenAI services.

Upgrade to Ruby 3.2 to avoid ReDoS attacks

Did you know that in Ruby 3.1.3 and prior some regular expressions could take a long time to process?

Don't believe me? Try running this in a 3.1.3 irb console:

/^a*b?a*$/ =~ "a" * 50000 + "x"

Your system will halt for like 10 seconds before returning no matches. This is the basis for ReDoS (Regexp Denial of Service) attacks.

Thankfully, Ruby 3.2.0 has fixed this and the same regexp gets resolved in 0.003 seconds. They also added a Regex.timeout global option which would prevent your app from falling victim to ReDoS attacks!

Enumerable#any? and Enumerable#detect

Someone told me #any? didn't stop iterating after finding the first truthy value, that I should use #detect instead, but turns out both methods behave the same, at least in Ruby 3.0.1.

Try running this for example:

(1..5).to_a.any? do |n|
  puts n
  true
end

(1..5).to_a.detect do |n|
  puts n
  true
end

You'll see this:

1
1

to install ruby 2.4.6 on a Mac m1 with rbenv

Once upon a time (even thouhg we have newer versions of ruby) I had to install an older version of ruby, the 2.4.6 on my Mac M1 (OS X 12.6), I was having this problem:

ruby-build: using readline from homebrew

BUILD FAILED (macOS 12.6 using ruby-build 20220910.1)

So after googling I found that we can do this:

CFLAGS="-Wno-error=implicit-function-declaration" RUBY_CONFIGURE_OPTS='--with-readline-dir=/usr/local/opt/readline/' arch -x86_64 rbenv install 2.4.6

and it worked!

that's it bye!

Always use retry inside rescue blocks

Ruby includes a retry keyword that would bring execution back to the start of the current method while preserving scope. But what if you do this?

def my_method
  my_var = 1
  retry
end

That would fail with an Invalid retry, which is one of the few illegal statements in Ruby.

retry is only allowed inside rescue blocks.

def my_method
  my_var = 1
  raise Exception
rescue Exception
  retry
end

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?

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]}

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.

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.

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

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

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

use bundle open gemname

You can take a peek at a locally installed gem's code very easily in ruby mine.
If you are using another IDE it would require a few extra steps:
First, set BUNDLER_EDITOR in ~/.bashrc or ~/.zshrc (or whatever shell you are using)
export BUNDLER_EDITOR="code"
(code to use VS code)
Then, simply run $ bundle open gemname and voilá! Look at whatever gem you want

Ruby 2.7 introduced numbered parameters for blocks

Since Ruby 2.7, it is possible to use numbered parameters in blocks additionally to named parameters.

This is an example of a block with named parameters:

my_array.each { |element| element.do_something }

And this is the same line with numbered parameters:

my_array.each { _1.do_something }

This works with multiple parameters too:

# named parameters
my_hash.each_pair { |key, value| puts "#{key}: #{value}" }

# numered parameters
my_hash.each_pair { puts "#{_1}: #{_2}" }

Creating an infinite loop in Ruby

If you need to iterate over a collection that its size is unknown, you can create an infinite loops in ruby easily, but if you need an index, then there's this option:

(1..).each do |page|
  results = ExternalService.get('/endpoint', page: page)
  parsed = JSON.parse(response.body)
  break if parsed['data'].empty?

  process_data(parsed['data'])
end

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

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

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]]

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

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}]

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"

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

a Ruby Module is a Class that can be subclassed

Great article at http://tech.degica.com/en/2017/10/03/rubykaigi/ by Chris Salzberg

some of the most interesting features of a language are not the ones that are cleverly designed, but the ones that emerge out of a clever design

Ruby has a safe operator, no need to try anymore

Wow, how did I miss this memo? Ruby 2.3 introduced a safe operator

Instead of

current_user.try(:profile).try(:bio)

you can now do

current_user&.profile&.bio

Use fetch method to access ENV variables in Ruby

It's better for at least a couple of reasons, eloquently stated in this blog post by Michal Orman

...we can set default values or handle - providing a block - gracefully missing keys. Also using fetch with unknown key will raise KeyError that will tell us which exactly key is missing. That is in fact the behavior we are expecting from the app. Without required settings is just not working and complaining about missing setting and not about some random nil references.

Got that? Instead of this:

AWS.config(
  access_key_id:      ENV['S3_ACCESS_KEY'],
  secret_access_key:  ENV['S3_SECRET_KEY'],
  region:             ENV['S3_REGION']
)

Do this:

AWS.config(
  access_key_id:      ENV.fetch('S3_ACCESS_KEY'),
  secret_access_key:  ENV.fetch('S3_SECRET_KEY'),
  region:             ENV.fetch('S3_REGION')
)

Keyword arguments in Ruby 2

Ruby 2 gained keyword arguments while I was away in Javascript-land the last few years, and somehow I missed the memo. Loving the elegance of how you specify defaults and required parameters.

Thoughtbot has the best writeup.

I suspect I will not be using non-keyword arguments in my Ruby code anymore.

Don't try to modify strings that come out of ENV

Today I woke up to an exception RuntimeError: can't modify frozen String

I looked at the code that had raised the exception. It was doing a gsub! on a value out of params. It took me a bit of digging to figure out why it failed. Turns out there's an edge case that causes one of those values to get pulled out of ENV instead of the normal flow where it originates from the user.

Strings coming out of ENV are frozen. D'oh!