Fix REPL of better_errors page
The gem better_errors offers a detailed error page with an interactive REPL for better debugging.
I had the issue that on a few projects with Ruby 2.5.8
, the REPL was not shown.
Solution
To make the REPL work properly with this Ruby version I had to update the gem binding_of_caller to at least version 0.8.0
.
From the [better_errors](https://github.com/BetterE...
Ruby: You can nest regular expressions
Ruby lets you re-use existing RegExp
objects by interpolating it into new patterns:
locales_pattern = /de|en|fr|es/i
html_tag_pattern = /<html lang="#{locales_pattern}">/
Any modifiers like /i
or /x
will be preserved within the interpolated region, which is pretty cool. So in the example above only the interpolated locales are case-insensitive, while the pattern around it (/<html .../
) remains case-sensitive.
RSpec matcher to compare two HTML fragments
The RSpec matcher tests if two HTML fragments are equivalent. Equivalency means:
- Whitespace is ignored
- Types of attribute quotes are irrelevant
- Attribute order is irrelevant
- Comments are ignored
You use it like this:
html = ...
expect(html).to match_html(<<~HTML)
<p>
Expected content
</p>
HTML
You may override options from CompareXML by passing keyword arguments after the HTML string:
html = ...
expect(html).to match_html(<<~HTML, ignore_text_nodes: true)
...
How to extract a Ruby gem
The rubygems binary gem
allows to extract a local gem with gem unpack GEMNAME
. For more details see the official documentation.
This was useful for compliance checks, when it was necessary to check the license of the C-files in nokogiri
.
Spreewald development steps
Our gem spreewald supports a few helpers for development. In case you notice errors in your Cucumber tests, you might want to use one of them to better understand the underlying background of the failure. The following content is also part of the spreewald's README, but is duplicated to this card to allow repeating.
Then console
Pauses test execution and opens an IRB shell with current cont...
How to avoid raising RestClient exceptions for 4xx or 5xx results
When using RestClient to make an HTTP request, it will raise an exception when receiving a non-successful response.
HTTP status codes like 422 or 403 might be totally expected when talking to APIs, so plastering your code with rescue RestClient::Exception
or similar can feel annoying.
It may not be intuitive, but the readme says you can also pass a block to methods like RestClient.get
or RestClient::Request.execute
. In that case, RestClient will not raise ...
Chromedriver: Disabling the w3c option might break your integration tests with Chrome 91
We recently noticed issues with Chrome 75+ when having the w3c
option enabled within the Selenium webdriver. It looks like recent Selenium versions don't have any issues with the w3c interface anymore. And starting with Chrome 91 this fix might cause unexpected issues, so you should try to enabled this option again or just remove the following line from you configuration:
options.add_option('w3c', false)
Background: Setting the w3c
option t...
Heads up: counting may be slow in PostgreSQL
The linked article points out that COUNT
queries might be unexpectedly slow in PostgreSQL.
If you just need to know "are there any records" use any?
. This uses SELECT 1 AS one FROM ... LIMIT 1
under the hood.
If you just need to know "are there no records" use empty?
or none?
. This uses SELECT 1 AS one FROM ... LIMIT 1
under the hood.
In short: Replace foo.count > 0
with foo.any?
or foo.count == 0
with foo.none?
If you require quick counts and can tolerate some level of imprecision, consider exploring the ...
Heads up: Byebug has problems with zeitwerk
I encountered a unlucky behavior of byebug 11.1.3 (the most recent version at time of writing) when using it with Rails 6 and it's new autoloading component, zeitwerk. There already is a issue for that, so I hope it will be fixed with a future release.
The following test succeeds:
context 'factories' do
let(:test_case) { FactoryBot.create(:test_case) }
it 'are valid' do
expect(test_case).to be_valid
end
end
But when I did the same in byebug the foll...
Ruby: Fixing strings with invalid encoding and converting to UTF-8
When dealing with external data sources, you may have to deal with improperly encoded strings.
While you should prefer deciding on a single encoding with the data-providing party, you can not always force that on external sources.
It gets worse when you receive data with encoding declaration that does not reliably fit the accompanying string bytes.
Here is a Ruby class that helps converting such strings to a proper encoding.
Note that it tries several approaches of changing the encoding. **This is not a silver bullet and may or may not work...
Pre-releasing an npm package
You can publish pre-release versions of an npm package.
Naming convention for pre-release versions
An npm package must use Semantic Versioning's naming convention for its version. In Semantic Versioning, the version number and pre-release identifier (like rc1
) must be separated by a dash, like this:
1.0.0-rc1
2.3.0-alpha2
3.0.0-beta3
Publishing to a pre-release tag
npm packages have multiple "current" releases, identified by "tags". The default tag is latest
. It is expected to contain the la...
Pre-releasing a Ruby gem
When a Ruby version gem has a letter in its version number, it is considered a pre-release:
1.0.0.rc1
2.3.0.alpha2
3.0.0.beta3
4.0.0.pre.rc2
Even if a pre-release gem has the highest version number, it is never installed unless the user explictily requested the version:
gem install foobar --version="=2.3.0.alpha2"
Also bundle update
will never update a stable version to a pre-release version unless the user explicitly requests it in the Gemfile
:
gem 'foobar', '=2.3.0.alpha2'
A note on Semanti...
Bundler: Packaging gems into the git repository (offline installation)
Installing gems on a server that has no access to the internet (especially rubygems.org
) requires to bundle the gems into the repository itself. This requires to adjust the bundle config in the repository.
- Execute the following commands to configure bundler:
bundle config set --local path vendor
bundle config set --local disable_shared_gems true
Note
For Bundler < 2 you have to omit the "set":
bundle config --local name value
.
See here: [https://bundler.io/v1.17/man/bundle-config.1.html](https://bundler.io/v1.17/man...
Capistrano: Deployment issue undefined method `[]' for nil:NilClass
In newer passenger versions the output of passenger -v
has changed. capistrano-passenger
tries to parse the version and now causes the error undefined method '[]' for nil:NilClass
. To fix this you only need to upgrade the capistrano-passenger
gem.
Therefore run bundle update capistrano-passenger --conservative
.
The version change of passenger from 6.0.7
to 6.0.8
has triggered this problem. This is fixed in capistrano-passenger >= 0.2.1
.
Changes to positional and keyword args in Ruby 3.0
Ruby 3.0 introduced a breaking change in how it treats keyword arguments.
There is an excellent blog post on the official Ruby blog going into the details. You should probably read that, but here is a slightly abbreviated version:
What changed
When the last argument of a method call is a Hash, Ruby < 3 automatically converted it to to Keyword Arguments. If you call a method in Ruby >= 3 that accepts keyword arguments, eithe...
Ruby: How to convert hex color codes to rgb or rgba
When you have a hex color code, you can easily convert it into its RGB values using plain Ruby.
>> "#ff8000".match(/^#(..)(..)(..)$/).captures.map(&:hex)
=> [255, 128, 0]
You can use that to implement a simple "hex to CSS rgba value with given opacity" method:
def hex_color_to_rgba(hex, opacity)
rgb = hex.match(/^#(..)(..)(..)$/).captures.map(&:hex)
"rgba(#{rgb.join(", ")}, #{opacity})"
end
>> hex_color_to_rgba("#ff8000", 0.5)
=> "rgba(255, 128, 0, 0.5)"
If you need to support RGBA hex color codes,...
E-mail deliverability
When your application is open for public sign up and sends out transactional e-mails to a large number of users, e-mail deliverability becomes an issue.
E-mail providers work hard to eliminate spam and have put in place relatively tight checks what kinds of emails they will accept, and from whom. To that end we use tools like mail-tester.com to make our mails as acceptable as possible. Unfortunately, there will always be providers that reject our e-mails for some reason or other, sometimes outside of our control.
For exa...
The Ruby Object Model
In Ruby (almost) everything is an Object
. While this enables a lot of powerful features, this concept might be confusing for developers who have been programming in more static languages, such as Java or C#. This card should help understanding the basic concepts of Ruby's object model and how things behave.
Usage of objects in Ruby
When working with objects in Ruby, you might think of a "container" that holds metadata, variables and methods. Metadata describes stuff like the object's class or its object_id
whi...
makandra/capybara-lockstep
capybara-lockstep can help you with flaky end-to-end tests:
This Ruby gem synchronizes Capybara commands with client-side JavaScript and AJAX requests. This greatly improves the stability of a full-stack integration test suite, even if that suite has timing issues.
Ruby: Generating and parsing JSON, or: understanding JSON::ParserError "unexpected token"
json
is part of the standard library of Ruby and deals with JSON, obviously. As you know, JSON is the string format that represents simple data structures. Ruby data structures that resemble Javascript objects can be serialized to JSON with #to_json
. These can be restored from a JSON string with JSON.parse()
.
So what could go wrong here?
JSON.parse("a".to_json)
It will raise JSON::ParserError (784: unexpected token at '"a"')
. But why?
Generating JSON vs serializing objects
J...
Parsing JSON with edge cases
The linked article shows that there are unclear parts in the JSON specification and that different parsers treat them differently (which could lead to security vulnerabilities in certain cases).
I was curious what Ruby does (Ruby 2.6.6 with gem json 2.3.0, implementing RFC 7159):
Duplicate Keys
irb(main):001:0> require 'json'
=> true
irb(main):002:0> JSON.parse('{"qty": 1, "qty": -1}')
=> {"qty"=>-1}
Character Collision
irb(main):009:0> JSON.parse('{"qty": 1, "qty\ud800": -1}')
JSON::ParserError (487: incomplete sur...
Webmock < 3.12.1 cannot handle IPv6 addresses correctly
We had the issue, that a VCR spec failed, after updating CarrierWave
from version 0.11.0
to 1.3.2
.
In this version, CarrierWave
uses the gem SsrfFilter
, which retrieves the IP addresses for the given hostname and replaces the hostname in the requested url with one of them.
It works with IPv4 addresses, but not with IPv6 addresses, because WebMock cannot handle those correctly:
uri = "#{protocol}://...
Too many parallel test processes may amplify flaky tests
By default parallel_tests will spawn as many test processes as you have CPUs. If you have issues with flaky tests, reducing the number of parallel processes may help.
Important
Flaky test suites can and should be fixed. This card is only relevant if you need to run a flaky test suite that you cannot fix for some reason. If you have no issues...
How to fix: WrongScopeError when using rspec_rails with Rails 6.1
tl;dr: Upgrade the gem to at least 4.0.1
When you use rspec_rails
in a version < 4 with Rails 6.1 you may encounter an error like this:
Failure/Error:
raise WrongScopeError,
"`#{name}` is not available from within an example (e.g. an " \
"`it` block) or from constructs that run in the scope of an " \
"example (e.g. `before`, `let`, etc). It is only available " \
"on an example group (e.g. a `describe` or `context` block)."
`name` is not available from within an example (e.g. an `it` block) or from constructs that...