Symbols can be strings
For what it's worth, I didn't know that symbols in Ruby can also be strings.
For example:
:"foo bar"
=> :"foo bar"
:"foo bar".class
=> Symbol
Thanks, Exercism.
For what it's worth, I didn't know that symbols in Ruby can also be strings.
For example:
:"foo bar"
=> :"foo bar"
:"foo bar".class
=> Symbol
Thanks, Exercism.
Yesterday I learned (YIL?!) how to override instance methods within pry
. But what if you like the change and want make it more permanent? Typically what I would do is edit the appropriate file in my editor, and either reload!
within pry
or restart the session.
I learned you can also edit the code directly from within pry
- if I want to edit the User
class, I can run edit User
and pry will open the file that defines User
in my $EDITOR
((n)vim, natch). When you close the file, you'll be dropped back into your pry
session and the code will be reloaded (including any instances of the class you already initialized in your session).
class User
def name = "tony"
end
pry(main)> me = User.new
pry(main)> me.name
# => "tony"
pry(main)> edit User # and let's say we edit name to return 'not-tony'
pry(main)> me.name
# => "not-tony"
pry(main)> User.new.name
# => "not-tony"
Today I learned you can override methods on an object from within pry
! Super helpful if you want to test something out but not change it in code yet. It's important to note this only overrides at the instance level, not the class level, so other instances won't be affected.
class User
def name = "tony"
end
pry(main)> me = User.new
pry(main)> me.name
# => "tony"
pry(main)> def me.name = "who now?"
pry(main)> me.name
# => "who now?"
pry(main)> other_me = User.new
pry(main)> other_me.name
# => "tony"
Today I learned you can write nested expectations in rspec
. I find this is approach useful with writing request-style tests where I want to ensure the request returned successfully and also ensure the expected effects happened.
it "creates the post" do
expect {
expect(request.response_code).to eq(200)
}.to change { Post.count }.by(1)
end
If either the response is not 200
, or the Post
count doesn't change, then the test fails.
There is a gotcha that I run into when I build these tests iteratively - first the inner expectation on it's own, wrap it in the outer block, and then add the outer matcher. If you wrap the inner request in an expect block, but don't have any assertions on that block, it will always pass - because we're not matching against anything.
RSpec.describe do
# ❌ - as expected
it do
expect(true).to eq(false)
end
# ❌ - as expected, outer expectation passes but inner fails, so test fails
it do
expect {
expect(true).to eq(false)
}.to not_change { 0 }
end
# ✅ - :-? watch out for this one, even though the inner expectation fails, test passes
it do
expect {
expect(true).to eq(false)
}
end
end
When rescuing exceptions with the intent to reraise them after doing some logic, you don't need to pass the original exception to the raise
method:
begin
# ...
rescue
puts "Something went wrong"
raise # will raise the original exception
end
I recently got an Enviro hat for my Raspberry Pi Zero W. And in tinkering with it, I found there's a ruby driver for interacting with the BME280 sensor.
You can use it like this -
require "i2c/device/bme280"
require "i2c/driver/i2c-dev"
I2CDevice::Bme280.new(driver: I2CDevice::Driver::I2CDev.new("/dev/i2c-1")).read_raw
In debugging, I learned a few useful commands to check the i2c port. Basically, I just needed to figure out what i2c port to use for the ruby driver.
Check if the i2c port is enabled (raspberry pi os)
sudo raspi-config nonint get_i2c # 0 if enabled, 1 if disabled
List all the i2c interfaces by ls
ing the dev node.
ls /dev/i2c*
Check if the i2c board interface is in the list of kernel modules
lsmod | grep i2c
A change to Ruby 3.4 will print a warning while using the verbose flag -w
when a method is passed a block and the block is not used by that method.
Here's our example file scratch.rb
-
def print_message(message) = puts message
print_message("hello_world") { puts "hello anonymous" }
> ruby scratch.rb
hello world
> ruby -w scratch.rb
scratch.rb:3: warning: the block passed to 'Object#print_message' defined at scratch.rb:1 may be ignored
hello world
https://www.ruby-lang.org/en/news/2024/12/25/ruby-3-4-0-released/
Today I learned you can nest FactoryBot traits within other traits. Traitception?!
Say you have a blog post model with a deleted_at
attribute, and an optional deleted_by
attribute. You could have:
FactoryBot.define do
factory :post do
trait :deleted do
deleted_at { Time.current }
end
trait :deleted_by_admin do
deleted
deleted_by { :admin }
end
end
end
There the deleted
in deleted_by_admin
references the deleted
trait above it.
You could alternatively define a new factory that composes the two traits, but it's always nice to have options.
factory :admin_deleted_post, traits: [:deleted, :deleted_by_admin]
I use the rspec change matcher a lot to check the before and after values of something while the subject under test executes. A convoluted example:
RSpec.describe "expect change from to" do
it do
x = 1
expect{ x = x + 1 }.to change { x }.from(1).to(2)
end
end
And sometimes I use to it to verify something doesn't change.
RSpec.describe "expect not to change" do
it do
x = 1
expect{ nil }.not_to change { x }
end
end
Which is great - but sometimes I want to make sure my understanding of the initial state is correct and want to verify it didn't change from its initial value - in this case 1.
RSpec.describe "expect not to change from" do
it do
x = 1
expect{ nil }.not_to change { x }.from(1)
end
end
And if that from
value is wrong, I'll get a nice message explaining what's wrong.
RSpec.describe "expect not to change from" do
it do
x = 1
expect{ nil }.not_to change { x }.from(0)
end
end
# => expected `x` to have initially been 0, but was 1
You can use comparison operators like <
, <=
, >
, and >=
(and more!) in rspec tests:
RSpec.describe "Comparison Operators" do
it do
expect(5).to be < 7
expect(5).to be >= 5
end
end
I ran into a problem where I wanted to use a case
statement to match against a regex, but also capture some values from the matched data.
Say I want to match on a pair of numbers wrapped in parens like (2,4)
and add them together.
If I match without capture groups I have to grab the numbers again in the when
block:
x = "(2,4)"
case x
when /\(\d+,\d+\)/
a,b = x.scan(/\d+/)
a.to_i + b.to_i
end
# => 6
Seems a shame to have perform another op to get a
and b
. What if we used capturing groups like /\((\d+),\(d+)\)/
? How do we refer to those captured values inside the when
? We can use some of ruby's special variables, namely $1
and $2
to refer to the captured groups of the last regexp.
x = "(2,4)"
case x
when /\((\d+),(\d+)\)/
$1.to_i + $2.to_i
end
# => 6
$1
and $2
feel a little magic, so we can name our capture groups group1
and group2
, and access those from $~
(the MatchData
of the last regexp).
x = "(2,4)"
case x
when /\((?<group1>\d+),(?<group2>\d+)\)/
$~[:group1].to_i + $~[:group2].to_i
end
# => 6
Let's say you want to check if a number is between 1 and 3 in ruby.
You can use #include?
:
(1..3).include?(2)
# => true
(1..3).include?(3)
# => true
(1..3).include?(5)
# => false
You can also use the threequals operator ===
:
(1..3) === 2
# => true
(1..3) === 3
# => true
(1..3) === 5
# => false
Both methods can be used on inclusive and exclusive ranges
Today I learned about casecmp
and casecmp?
to compare strings in ruby.
casecmp
compares the downcase of both strings and returns 1
if the compared string is smaller, -1
if it's larger, and 0
if they are equal (and nil
if they can't be compared).
"hashrocket".casecmp("hashrocket") # => 0
"hashrocket".casecmp("hAsHrOcKeT") # => 0
"hashrocket".casecmp("hashrocket123") # => -1
"hashrocket".casecmp("hashrock") # => 1
"hashrocket".casecmp(123) # => nil
casecmp?
does the same comparison but just returns a boolean.
"hashrocket".casecmp("hAsHrOcKeT") # => true
"hashrocket".casecmp("hashrock") # => false
"hashrocket".casecmp(123) # => nil
h/t Brian Dunn
Did you know you can look up ruby documentation on Classes and methods from the command line? The slightly elusive ri
command does just that. You can pass it an argument of the class/method you want to look up, or you can enter interactive mode without arguments.
$ ri uniq
$ ri Array#compact
$ ri Hash
You can also use it to browse all the pre-defined ruby global variables:
$ ri ruby:globals
Check out the docs or run ri --help
to see all it can do.
h/t Brian Dunn
Say you run your entire rspec suite and a couple of tests fail. You make a change that should fix them. How can you quickly rerun those failed tests to see if they're green? It could take minutes to run the whole suite again, and all you care about is 2 tests.
That's where the --next-failure
(-n
) flag comes in handy. According to the docs it is "Equivalent to --only-failures --fail-fast --order defined
)". So you can rerun only your failed specs, and exit immediately if one does fail. You could of course just use --only-failures
too, but sometimes it's nice to fail fast.
bundle exec rspec -n
h/t Brian Dunn
Today I learned there are two ways to construct a
range in ruby. Using two dots ..
creates a range including the start and end values.
(2..5).include?(2) # => true
(2..5).include?(5) # => true
Using three dots ...
creates a range including the start value, but not the
end value.
(2...5).include?(2) # => true
(2...5).include?(5) # => false
So if we think of them in terms of
intervals, (a..b)
is
a closed interval ([a, b]
), and (a...b)
is a right half-open interval ([a, b)
).
Ruby has a new-ish class to build "immutable" structs, check this out:
Measure = Data.define(:amount, :unit)
distance = Measure.new(100, 'km')
distance.amount
#=> 100
distance.unit
#=> "km"
And if you try to use a setter, then it will fail:
distance.amount = 101
(irb):7:in `<main>': undefined method `amount=' for an instance of Measure (NoMethodError)
Ruby has a new-ish method to count occurrences in an array. So since ruby 2.7.0 you can use the Enum tally to do:
["a", "b", "c", "b"].tally
# => {"a"=>1, "b"=>2, "c"=>1}
Here's a similar function in Elixir
Today I learned you can use regular expressions in RSpec method argument expectations.
Suppose I have a method that takes an email, and a registered boolean as parameters:
def some_method(email:, registered:)
end
In a spec, it's easy enough to verify that it was called with set parameters, like test@example.com
and true
:
expect(subject)
.to receive(:some_method)
.with(email: 'test@example.com', registered: true)
But what if I want to verify that the email address just belongs to example.com
? We can use a regex for that!
expect(subject)
.to receive(:some_method)
.with(email: /@example.com$/, registered: true)
Today I learned that Ruby has an alias for Array slice method, basically you can use the []
the same way you'd use the slice
method:
array = [ "a", "b", "c", "d", "e" ]
array[3..5]
# => ["d", "e"]
array.slice(3..5)
# => ["d", "e"]
I found out that's possible to change the FactoryBot strategy by invoking the to_create method inside the factory
definition.
We had to do that to make factory bot linting to work on a factory that acts like an enum. So we did something like this:
FactoryBot.define do
factory :role do
to_create do |instance|
instance.attributes = instance.class
.create_with(instance.attributes)
.find_or_create_by(slug: instance.slug)
.attributes
instance.instance_variable_set(:@new_record, false)
end
end
end
The said part here is that FactoryBot expects us to mutate that instance
in order to work.
I recently had to deal with parsing Julian Dates on a project. This was my first time seeing this in the wild and to my surprise, Ruby has some handy utilities on Date for converting to/from Julian Date Numbers.
Parse a Julian Date Number into a Date
Date.jd(2459936)
#=> #<Date: 2022-12-22 ((2459936j,0s,0n),+0s,2299161j)>
Convert a Date Object to a Julian Date Number
Date.new(2024, 02, 29).jd
#=> 2460370
By default, some numbers in strftime are padded, either with 0 or ' '
.
For example:
best_moment_ever = DateTime.new(1996, 2, 15, 19, 21, 0, '-05:00')
=> Thu, 15 Feb 1996 19:21:00 -0500
best_moment_ever.strftime("%m/%e/%Y at %l:%M%P")
=> "02/15/1996 at 7:21pm"
As we can see, there is a big gap between at
and 7:21pm
. This is because the hour is being padded with empty string. Sometimes this is fine, but if you ever wanted to remove any padding, just add a -
flag to the directive:
best_moment_ever.strftime("%-m/%e/%Y at %-l:%M%P")
=> "2/15/1996 at 7:21pm"
Notice how we also removed other built in padding, like the 0 in the month
There's a few other ways you can manipulate the results. Learn more here!
Today I learned RSpec has a be_within matcher.
It does what it says - it verifies if the actual is within a delta (<=
)
of the expected.
expect(3.0).to be_within(0.5).of(3.0) # ✅
expect(3.2).to be_within(0.5).of(3.0) # ✅
expect(3.5).to be_within(0.5).of(3.0) # ✅
expect(4.3).to be_within(0.5).of(3.0) # ❌
Bundler allows you to pass group options when running commands in the terminal:
bundle outdated --group test
bundle upgrade --group test
Have you ever wanted to grab the first n
elements from an array?
You might think to do something like this:
fruit = ["apple", "banana", "blueberry", "cherry", "dragonfruit"]
# Grab the first 3 elements of the array
fruit[0...3]
=> ["apple", "banana", "blueberry"]
Well you could just use Array#take
and tell it how many elements you want to take:
fruit = ["apple", "banana", "blueberry", "cherry", "dragonfruit"]
# Grab the first 3 elements of the array
fruit.take(3)
=> ["apple", "banana", "blueberry"]
Bonus:
There is also Array#take_while
which takes a block and passes elements until the block returns nil
or false
fruit.take_while {|f| f.length < 8 }
=> ["apple", "banana"]
fruit.take_while {|f| f.length < 10 }
=> ["apple", "banana", "blueberry", "cherry"]
Ever wanted map_with_index
, like each_with_index
except for map
instead of each
? Turns out you can, with just a 1 character change. with_index
is a method on Enumerator
that lets you do just that:
['a', 'b', 'c'].map.with_index do |x, index|
[x, index]
end
#=> [["a", 0], ["b", 1], ["c", 2]]
If you want to search for a pattern in a string and get back all the matches of that pattern, you can use String#scan
:
irb(main)> "..123...456...123".scan(/\d+/)
=> ["123", "456", "123"]
This is super useful. But sometimes, it would be even more useful to also know the index of where the match starts. Turns out, you can do this with $~
irb(main)> matches_with_index = []
irb(main)* "..123...456...123".scan(/\d+/).map do |x|
irb(main)* [x, $~.offset(0)[0]]
irb(main)> end
irb(main)> matches_with_index
=> [["123", 2], ["456", 8], ["123", 14]]
$~
is a global variable that's equivalent to Regexp.last_match
, which is the MatchData
for the last successful pattern match - it basically lets you get some data about the last thing Regexp
matched.
MatchData#offset
returns an array with the starting and ending offsets of the match. So $~.offset(0)[0]
-> the offset to the start of the match, and $~.offset(0)[1]
-> the offset to the end of the match.
Today I Learned ruby has a lot of abbreviated assignment operators.
The best known are +=
and -=
to increment and decrement values:
x = 2
x += 1
x #=> 3
And of course there's ||=
, to assign only if the value is nil
or false
:
x = nil
x ||= 4 #=> 4
x ||= 5 #=> 4
But these abbreviations can be applied to a lot more operators!
It works with all of the following: +
, -
, *
, /
, %
, **
, &
, |
, ^
, <<
, >>
, &&
, ||
.
So we could use |=
to union two arrays and assign the result to the variable:
x = [1, 2, 3]
x |= [2, 3, 4, 4]
x #=> [1, 2, 3, 4]
Symbols are an integral part of Ruby… we use them everyday. However sometimes they can be used for identification where we use their stringified version for comparison.
input = “wasabi”
:wasabi.to_s == input
=> true[[]]
Every time we do this, a new string is allocated in memory as the representation of :wasabi
. For a trivial example like this, it’s not a big deal but consider how often Ruby on Rails uses symbols (HashWithIndifferentAccess
anyone?). Then the bloat becomes very real.
Introduced in Ruby 3.0, Symbol#name(https://github.com/ruby/ruby/pull/3514) aims to help. Utilizing this method returns a frozen string version of the symbol.
:wasabi.name
=> “wasabi”
This looks like we’re reproducing the same result and in a way we are. However due to the returned string being frozen, there is only one immutable instance of it being used in memory!
It can be used the same way as well now with less memory bloat.
input = “wasabi”
:wasabi.name == input
=> true
A new method definition was introduced in Ruby 3.0, the endless definition.
You're probably familiar with:
def do_something(number)
number * 2
end
Of course, we can express this as a one-liner previously as:
def do_something(number); number * 2; end
Now you have the option to write it like this:
def do_something(number) = number * 2
Or another example:
def thing(x) = @thing = x
If you'd like to know more, here is where the spec was discussed
Ruby blocks offer a shorthand to positional block variables.
Normally you'd see a block like this:
[1, 2, 3].each { |number| do_something(number) }
However with Numbered Parameters, introduced in Ruby 2.7, we can express this like so:
[1, 2, 3].each { do_something(_1) }
The _1
takes the place of an explicitly defined block variable.
TIL in rspec you can yield a double to a block with and_yield
, similar to how you return a double with and_return
.
With and_return
you can write a test like this:
sftp = Net::SFTP.start(args)
sftp.upload!(content, path)
# Test
client = double
allow(Net::SFTP).to receive(:start).and_return(client)
expect(client).to receive(:upload!)
However, if your code has a block like below and_return
won't work. Instead, you can use and_yield
to yield the double to the block:
Net::SFTP.start(args) do |sftp|
sftp.upload!(content, path)
end
# Test
client = double
allow(Net::SFTP).to receive(:start).and_yield(client)
expect(client).to receive(:upload!)
Given you have an array of objects that you may want to split apart based on a value on one of the objects, you can use slice_after
(there's also slice_before
, which behaves the same way).
array = [
{activity: "traveling", ticket: "123"},
{activity: "working", ticket: "123"},
{activity: "awaiting_assignment", ticket: ""},
{activity: "traveling", ticket: "234"},
{activity: "refueling", ticket: "234"},
{activity: "traveling", ticket: "234"},
{activity: "working", ticket: "234"},
{activity: "awaiting_assignment", ticket: ""}
]
array.slice_after { |i| i.activity == "awaiting_assignment" }
# Returns:
[
[
{activity: "traveling", ticket: "123"},
{activity: "working", ticket: "123"},
{activity: "awaiting_assignment", ticket: ""}
],
[
{activity: "traveling", ticket: "234"},
{activity: "refueling", ticket: "234"},
{activity: "traveling", ticket: "234"},
{activity: "working", ticket: "234"},
{activity: "awaiting_assignment", ticket: ""}
]
]
As you probably already know, in Ruby, you can decompose a nested array into variables like so:
letters_and_numbers = [["a", "b", "c", "d", "e"], [1, 2, 3, 4, 5]]
letters, numbers = letters_and_numbers
>> letters
=> ["a", "b", "c", "d", "e"]
>> numbers
=> [1, 2, 3, 4, 5]
However, did you also know that you can add parentheses ()
to decompose specific values from a nested array?
letters_and_numbers = [["a", "b", "c", "d", "e"], [1, 2, 3, 4, 5]]
(a, b, *other_letters), numbers = letters_and_numbers
>> a
=> "a"
>> b
=> "b"
>> other_letters
=> ["c", "d", "e"]
>> numbers
=> [1, 2, 3, 4, 5]
Note: You can also grab values from either the beginning or end of the array!
letters_and_numbers = [["a", "b", "c", "d", "e"], [1, 2, 3, 4, 5]]
(a, *other, d, e), _ = letters_and_numbers
>> a
=> "a"
>> other
=> ["b", "c"]
>> d
=> "d"
>> e
=> "e"
It's not often there's a javascript feature I wish was available in ruby, but here we are. But, it turns out ruby has the functionality as of 2.7 and I was just out of the loop.
In javascript you can use destructuring assignment to unpack a bunch of variables in a single line:
const obj = { a: 1, b: 2, c: 3, d: 4 }
const { a, b, d: newName } = obj
console.log([a, b, newName])
// => [1, 2, 4]
With rightward assignment you can do a similar thing with hashes, though with slightly different syntax:
hsh = { a: 1, b: 2, c: 3, d: 4 }
hsh => { a:, b:, d: new_name }
puts [a, b, new_name]
# => [1, 2, 4]
Nice!
If you have been directly working or debugging within your gems and wish to revert any changes made, instead of manually undoing all of the changes in each file, you can simply run gem pristine <gem_name>
. This command restores installed gems to their original, pristine state using the files stored in the gem cache.
If you want to check out what options you can pass to it, here is some documentation.
H/T Matt Polito
factory_bot has an option to skip calling save!
on create:
FactoryBot.define do
factory :model_without_table do
skip_create
an_attribute { "An Attribute" }
end
end
This will build the object in memory, but not persist it. Useful if you want to create a factory for a model that isn't backed by a database table, where trying to persist the record would result in an exception.
Most of the time when I write an RSpec test to see if a message was received, I'll write the expectation first using a mock, then exercise the subject under test:
class SomeJob
def perform
SomeService.call
end
end
Rspec.describe SomeJob do
it "makes the call" do
expect(SomeService).to receive(:call)
SomeJob.new.perform
end
end
Sometimes it's useful to flip this around, with the expectation after the action was performed. We can do this with a spy - we first stub the call with allow
, exercise the subject under test, then assert with have_received
:
class SomeJob
attr_reader :some_attribute
def perform
set_an_instance_var
SomeService.call(some_attribute)
end
def set_an_instance_var
@some_attribute = :something
end
end
Rspec.describe SomeJob do
subject { SomeJob.new }
it "makes the call with an argument" do
allow(SomeService).to receive(:call)
subject.perform
expect(SomeService).to have_received(:call).with(subject.some_attribute)
end
end
This is particularly useful if you want to assert against something (say an instance variable) that doesn't get set until the subject is exercised. There's no way to test the above example with the assertion first, since we won't know what some_attribute
is until we perform
- a perfect use case for a spy.
h/t Matt Polito
So you've done some configuration of your rubygems via the .gemrc
file but now you want to bypass that.
The --norc
flag will do just that:
gem install decent_exposure --norc
Now any configuration in your .gemrc
will be ignored.
For more info, check out the Rubygems documentation
I always forget to disable generation of gem documenatation until I see it getting generated during install :-(
Do yourself a favor and create a .gemrc
if you don't already have one and add:
gem: --no-document
Now all of your gem installs will be speedier and take up less space.
Some of you may remember --no-ri
& --no-rdoc
, however --no-document
takes care of both.
View the gem documentation for more info
Ever run into ruby's multiline autocomplete getting in the way while you're in a Heroku console?
Try opening your console this way:
heroku run "rails console -- --nomultiline"
Now it'll be disabled for that console session.
Say you have some date-specific functionality, and you want to test for a specific day of the week.
Date#commercial
is what you're looking for! It will create a Date object for you based on the year,week, and day that you give it.
require 'date'
# Wednesday (3) of week 1 of year 2023
Date.commercial(2023, 1, 3)
In Rails we can take this a step further, for example, to get Friday of this week:
Date.commercial(Date.current.year, Date.current.cweek, 5)
In your testing you can simply make use of the Rails TimeHelpers to travel to that specific date you need:
next_friday = Date.commercial(Date.current.year, Date.current.cweek + 1, 5)
travel_to next_friday do
# Friday specific code
end
When receiving a JSON payload it can, of course, be useful to see it in a more readable way. Turns out there is a built in utility in Ruby that can help with this.
Utilizing the Kernal#j
method:
foo = {name: "Matt", company: "Hashrocket"}
=> j foo
{"name": "Matt", "company": "Hashrocket"}
Utilizing the Kernal#jj
method:
foo = {name: "Matt", company: "Hashrocket"}
=> jj foo
{
"name": "Matt",
"company": "Hashrocket"
}
I had a bit of trouble trying to find docs on how to do a curl --upload-file
request with ruby. This flag is a special flag that tells curl
to generate a PUT
request with the body being the file(s) to upload to the remote server.
In my case, I wanted to upload a single file, and I accomplished this with the faraday
and faraday-multipart gem
:
require 'faraday'
require 'faraday-multipart'
conn = Faraday.new("https://example.com") do |f|
f.request :multipart
end
upload_file = File.open("./path/to/image.jpg")
conn.put("/file-upload") do |req|
req['Content-Type'] = 'image/jpeg'
req['Content-Length'] = upload_file.size.to_s
req.body = Faraday::Multipart::FilePart.new(
upload_file,
'image/jpeg'
)
end
Part of the magic here is that you need to explicitly set the Content-Type
and the Content-Length
header.
When using the rackup gem, you can set the port with the --port
flag
rackup --port 4000
[2023-02-18 14:28:17] INFO WEBrick 1.8.1
[2023-02-18 14:28:17] INFO ruby 3.1.2 (2022-04-12) [x86_64-linux]
[2023-02-18 14:28:17] INFO WEBrick::HTTPServer#start: pid=62732 port=4000
%w can construct space delimited word arrays like this
%w(my cool word array)
#=> ["my", "cool", "word", "array"]
%W works similarly, however it offers ways to interpolate with variables and escape special characters in the assignment, while %w does not.
street_name = 'Sesame Street'
%W(I live on #{street_name})
#=> ["I", "live", "on", "Sesame Street"]
One of the challenges of using cucumber is properly managing your step definitions. Left unchecked, you will eventually have many unused steps. It's extremely cumbersome to prune these manually. Luckily, you can use cucumber's -f / --format
flag to get feedback on unused step_definitions and their locations:
bundle exec cucumber --dry-run --format=stepdefs
If your step definition is unused, it will be annotated with a line under that says NOT MATCHED BY ANY STEPS
. See the example -
/^I submit the proposal request form$/ # features/step_definitions/contact_steps.rb:39
NOT MATCHED BY ANY STEPS
As Ruby developers, we're often looking for ways to reduce time consuming lookups in our code. A lot of times, that leads us to memoizing those lookups with the common ||=
operator.
However, if our lookups return a nil or falsey value, our memo will actually keep executing the lookup:
def ticket
@ticket ||= Ticket.find_by(owner:)
end
This code essentially boils down to:
def ticket
@ticket = @ticket || Ticket.find_by(owner:)
end
If our find_by
in the example above returns nil, the code will continue to run the find_by
every time we call the ticket
method.
To avoid this, we can shift our pattern a bit, and look to see if we have already set our instance variable or not:
def ticket
return @ticket if defined?(@ticket)
@ticket = Ticket.find_by(owner:)
end
Enumerable#partition is a method that returns two arrays.
The first array is everything the block evaluated to true, and the second array contains everything that was false.
numbers = [2, 95, 24, 27, 85, 4]
even, odd = numbers.partition { |num| num.even? }
=> [[2, 24, 4], [95, 27, 85]]
even
=> [2, 24, 4]
odd
=> [95, 27, 85]