did_you_know.ruby?
ivo anjo ruby ninja @ talkdesk
follow along @
https://tinyurl.com/i-cant-read-from-here
Let's talk function arguments
get_contacts(10, true) ...?
deleted?
active?
with_phone?
page?
limit?
Documentation!!!
get_contacts(
start_from: 10,
has_address: true,
)
get_contacts(10, true)
Less derping/bugs!!!
get_contacts(true, 10)
?????
Extensibility!!!
get_contacts(10, true,
nil, nil, :married)
??????????
When to use
...my rules of 👍
• Hey! A bullet point
• Face it: you **were**
beginning to miss them
Now srsly
Methods from other classes •
Whenever **helpful** in • private methods
How to code 'em
def get_contacts(options = {})
start_from =
options.fetch(:start_from)
has_address =
options.fetch(:has_address)
Ruby >= 2.1
Kudos to the ones already updated, you rock guys!
def get_contacts(
start_from:, has_address: true)
required
optional
Variable Args?
def get_contacts(
start_from:, has_address: true,
**options)
get_contacts(start_from: 10,
foo: 'yes')
not allowed without **
Yes, you can (still) do this!
def get_contacts(**options)
start_from =
options.fetch(:start_from)
avoid unless you really need to
And it has a name!
**double splat**
You can combine with positional stuff
def print_hello(name, language:)
# ...
end
print_hello('Joe',
language: 'en_US')
The most complex method signature you will ever see
def foo(a, *b, c = true, d:,
e: true, **f, &block)
a ➡️ required positional
b ➡️ vararg positional
c ➡️ optional positional
The most complex method signature you will ever see
def foo(a, *b, c = true, d:,
e: true, **f, &block)
d ➡️ required keyword
e ➡️ optional keyword
f ➡️ vararg keyword
The most complex method signature you will ever see
def foo(a, *b, c = true, d:,
e: true, **f, &block)
block ➡️ duh
def foo(a, *b, c = true, d:,
e: true, **f, &block)
foo('a', 'b1', 'b2', 'b3', false,
d: 'd', e: false, f1: 'f1',
f2: 'f2') do puts 'Hello!' end
a ➡️ 'a'
b ➡️ ['b1',
'b2', 'b3']
c ➡️ false
d ➡️ 'd'
e ➡️ false
f ➡️ {f1: 'f1',
f2: 'f2'}
These rules (mostly) apply to blocks too
def foo
yield a:1
end
foo do |a:, b: 2|
puts a + b
end
But wait, there's more!
class Foo
def self.print(options = {})
name = options.fetch(:name)
puts name
end
def self.print_joe
print(user: 'Joe')
end
end
RSpec.describe Foo do
describe '#print_joe' do
it 'calls print' do
expect(Foo).to \
receive(:print).with(user:'Joe')
Foo.print_joe
end
end
end
Yay, my spec passes!
Nevermind the code actually being broken
class Foo
def self.print(options = {})
name = options.fetch(:name)
puts name
end
def self.print_joe
print(user: 'Joe')
end
end
Rspec knows kung-fu
...and keyword args
def self.print(name:)
puts name
end
That brings me to rspec doubles
d = double('Foo', print: nil)
d.print(user: 'Joe')
...verifying doubles!
d = class_double('Foo',
print: nil)
d.print(user: 'Joe')
Keyword args are just an example
Verifying doubles check number of positional args and if the method exists too.
https://relishapp.com/rspec/rspec-mocks/v/3-4/docs/verifying-doubles
• instance_double
• class_double
• o hai, more bullets!?
IF YOU REMEMBER ANYTHING
(e.g. if you dozed off around the "Hello I'm Ivo" part)
Keyword arguments & Verifying doubles
Oh and I almost forgot to tell you can use splat like this
rgb = [:red, :green, :blue]
cmy = [:cyan, :magenta, :yellow]
colors =
[:white, :black, *rgb, *cmy]
# colors is now
[:white, :black, :red, :green,
:blue, :cyan, :magenta, :yellow]
And you can use it on method calls too
def do_stuff(a, b, c)
# ...
end
some_args = ['b', 'c']
do_stuff('a', *some_args)
You can do the same with the double splat
def do_stuff(a:, b:, c:)
# ...
end
some_args =
{b: "not_the_b's", c: 'c'}
do_stuff(a: 'a', **some_args)
Merging hashes like a sir
def build_event(data, meta)
{**data, **meta}
end
But wait, there's more!
Pry is your friend!
To really confuse you, I'm going back to rspec again
class Bar
def self.hello
message =
ENV['MESSAGE'] || 'hello'
puts message
end
end
describe '#hello' do
it 'prints hello' do
expect(Bar).to \
receive(:puts).with('hello')
Bar.hello
end
it "when ENV['MESSAGE'] is set
prints the mssage" do
ENV['MESSAGE'] = 'hello world'
expect(Bar).to \
receive(:puts)
.with('hello world')
Bar.hello
end
Nice! Time to open that PR
DAT TYPO
:(
Well I fixed it and re-ran the specs
Random spec ordering! ... :| ? Well, how do I debug this???
Ask me for help :)
Let's try this again: Ask **rspec** for help
$ bundle exec rspec --bisect
Bisect started
Running suite to find failures... (0.16527
seconds)
Starting bisect with 1 failing example and
1 non-failing example.
Checking that failure(s) are order-dependent
... failure appears to be order-dependent
Bisect complete! Reduced necessary non-fail
ing examples from 1 to 1 in 0.15835 seconds.
The minimal reproduction command is:
rspec ./bar_spec.rb[1:1:1,1:1:2]
Especially useful when issue is cross-spec
interference
AKA I know this example was a bit trivial.
I don't care :D
Pry is your friend
irb is dead to me
You can use it to debug too
require 'pry'; binding.pry
Inspect, interact, call, ...
ls -- list methods and variables
show-source: where does this come from, and what is it?
It has tab completion!
Such tabbing!
break/step/next/finish/continue/frame/...
...using pry-byebug
gem 'pry'
# <-- just add this to gemfile
gem 'pry-byebug'
rubular.com for all your regexp stuffies
Shiny stuff you can do with Ruby 2.3
foo = {a: {b: {c: {d: :foo}}}}
foo.dig(:a, :b, :c, :d) # ➡️ :foo
foo.dig(:erm, :oops, :derp)
# ➡️ nil
# You can simplify this
foo && foo.bar && foo.bar.baz
# Using the safe navigation
# operator
foo&.bar&.baz
Ruby & Threads
• mri runs one thread at a time • but you can still cause mayhem
Ruby & Threads
• be careful with threads and threaded webservers (puma) • common mistake: require is *NOT* thread-safe -- don't use it in the middle of a method!!!!
• in concurrent-ruby we trust:
https://github.com/ruby-concurrency/concurrent-ruby
Oh and FYI on Rubies
• mri
• jruby
• jruby+truffle
• rubyomr
• rubinious
My bet's
on this one
Tired yet?
Friendly reminder: Ruby 2.3.1 is out Ruby < 2.1 is EOL
UPGRADE YOUR RUBY
Get Elixir today
NEXT EPISODE
git
Thanks! You guys r awesome learned lots from y'all