How to replace a key in a Ruby hash
Deleting a key from a hash returns the value and you can quickly assigning it to a new key on the same hash
test[:questions] = test.delete('my_questions')
Deleting a key from a hash returns the value and you can quickly assigning it to a new key on the same hash
test[:questions] = test.delete('my_questions')
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
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
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"
number = 4.9999999
number.toFixed(6) => '5.000000'
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!
#!/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
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
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.
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!
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.
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.
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!
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
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!
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
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?
The spaceship operator compares two objects (from left to right), returning either -1, 0, or 1.
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
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"]
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]}
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.
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
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.
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
module_function
to use all the methods inside a module as class methods ormodule_function :name
to only apply it in a specific methodAnother option to do so is using extend self
instead:
module User
extend self
def name
'Hello Sam'
end
end
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.
$ 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
$ typeprof skill.rb
# TypeProf 0.12.0
# Classes
class Skill
private
def acceptable_image: -> nil
end
$ 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
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')
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
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}" }
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
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
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.
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
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]]
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
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}]
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"
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
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
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
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 raiseKeyError
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')
)
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.
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!