Thread-safe collections in Ruby
When using threads, you must make your code thread-safe. This can be done by either locking (mutexes) all data shared between threads, or by only using immutable data structures. Ruby core classes like String
or Array
are not immutable.
There are several gems providing thread-safe collection classes in Ruby.
concurrent-ruby
The concurrent-ruby gem provides thread-safe versions of Array
and Hash
:
sa = Concurrent::Array.new # supports standard Array.new forms
sh = Co...
JavaScript: Working with Query Parameters
tl;dr: Use the URLSearchParams
API to make your live easier if you want to get or manipulate query parameters (URL parameters).
URLSearchParams
API
The URLSearchParams
API is supported in all major browsers except IE 11.
It offers you a bunch of useful methods:
-
URLSearchParams.append()
- appends a query parameter -
URLSearchParams.delete()
- deletes the specified query parameter -
URLSearchParams.get()
- returns the value of the specified query parameter - `URLSearchP...
Faking and testing the network with WebMock
An alternative to this technique is using VCR. VCR allows you to record and replay real HTTP responses, saving you the effort to stub out request/response cycles in close details. If your tests do require close inspection of requests and responses, Webmock is still the way.
WebMock is an alternative to FakeWeb when testing code that uses the network. You sh...
ActionMailer: Previewing mails directly in your email client
In Rails, we usually have a mailer setup like this:
class MyMailer < ActionMailer::Base
def newsletter
mail to: 'receiver@host.tld',
from: 'sender@host.tld',
subject: 'My mail'
end
end
If you want to preview your mail in the browser, you can use the Action Mailer Preview. To inspect the mail directly in your email client, just create an .eml
file and open it with your client:
mail = MyMailer.newsletter
Fil...
Jasmine: Fixing common errors during initialization
Due to the way we setup Jasmine tests in our projects, you may run into various errors when Jasmine boots.
Setting jasmineRequire
on undefined
Jasmine 4 may fail with an error like this:
Uncaught TypeError: Cannot set properties of undefined (setting 'jasmineRequire')
This is due to issues in Jasmine's [environment detection](https://github.com/jasmine/jasmine/blob/502cb24bb89212917a3c943b593fd918ffc481cb/lib/jasmine-core/...
Rails routing: Using constraints to avoid "Missing template" errors
You can use constraints in your routes.rb
to avoid getting errors when wrong routes are called. Instead, the user will see a 404.
If you want multiple routes to use the same constraint you can use the block syntax:
constraints(format: 'html') do
resources :pages
resources :images
end
If you want constraints only on certain routes, you can do:
get '/users/account' => 'users#account', constraints: { format: 'html' }
Note: You can also avoid this error through [format constraints in your controllers](/makandra...
Components: Dynamically growing input field's height to fit content
Sometimes you will need an input field which wraps content and grows in height as soon as content gets longer than the input fields width.
There is no way to get a "normal" string input field to wrap as desired, but there are other ways.
Here is one pretty easy solution to get what you want:
Step 1
Let your input became a text area with one row.
f.text_area(:name, rows: 1, autosize: '')
Step 2
Include the autosize library in your project
yarn add autosize
And make your...
Casting ActiveRecord scopes or instances to ActiveType extended model classes
When working with ActiveType you will often find it useful to cast an ActiveRecord instance to its extended ActiveType::Record
variant.
Starting with active_type 0.4.0 you can use ActiveType.cast
for this:
class User < ActiveRecord::Base
...
end
class SignUp < ActiveType::Record[User]
...
end
user = User.find(1)
sign_up = ActiveType.cast(user, SignUp)
sign_up.is_a?(SignUp) # => true
This is basically like [ActiveRecord#becomes
](http://apidock.com/rails/v4.2.1/ActiveRecord/...
Jasmine: Testing if two arrays contain the same elements
When the order matters:
expect(array1).toEqual(array2)
Regardless of order:
expect(array1).toEqual(jasmine.arrayWithExactContents(array2))
Ignoring extra elements:
expect(array1).toEqual(jasmine.arrayContaining(array2))
Version 5 of the Ruby Redis gem removes Redis.current
Redis.current
will be removed without replacement in redis-rb
5.0.
Version 4.6.0 adds deprecation warnings for Redis.current
and Redis.current=
:
`Redis.current=` is deprecated and will be removed in 5.0.
If your application still uses Redis.current
, you can only fix it by no longer using it. Here is how.
Redis.new when you need it
You can easily instantiate a Redis
client when you need it.
There is probably already a constant like REDIS_URL
that you use to configure Sidekiq or similar. So just use that one.
``...
Matching unicode characters in a Ruby (1.9+) regexp
On Ruby 1.9+, standard ruby character classes like \w
, \d
will only match 7-Bit ASCII characters:
"foo" =~ /\w+/ # matches "foo"
"füü" =~ /\w+/ # matches "f", ü is not 7-Bit ASCII
There is a collection of character classes that will match unicode characters. From the documentation:
-
/[[:alnum:]]/
Alphabetic and numeric character -
/[[:alpha:]]/
Alphabetic character -
/[[:blank:]]/
Space or tab -
/[[:cntrl:]]/
Control character -
/[[:digit:]]/
Digit -
/[[:graph:]]/
Non-blank character (excludes spaces...
Carrierwave: How to attach files in tests
Attaching files to a field that is handled by Carrierwave uploaders (or maybe any other attachment solution for Rails) in tests allows different approaches. Here is a short summary of the most common methods.
You might also be interested in this card if you see the following error in your test environment:
CarrierWave::FormNotMultipart:
You tried to assign a String or a Pathname to an uploader, for security reasons, this is not allowed.
If this is a file upload, please check that your upload form is multipart encoded.
Factor...
How to access Chrome Devtools when running JavaScript tests via CLI
While we are used to run our JavaScript tests on a test page within our Browser, it's also possible to run them on the command line with NodeJS. I think that's actually the most common way to run JS tests.
Given a Vue project that uses Jest (via vue-cli-service
) with the following package.json
:
{
"scripts": {
"test": "vue-cli-service test:unit --testMatch='**/tests/**/*.test.js' --watch"
},
}
This allows us to run J...
Jest: How to clear the cache
I recently was in a weird situation where my (Jest/CLI) tests were referencing a function that was no longer part of my code - I had just refactored it.
Apparently Jest has some kind of cache that caused the issue, running npx jest --clearCache
solved it for me.
RSpec: You can super into parent "let" definitions
RSpec's let
allows you to super
into "outside" definitions, in parent contexts.
Example:
describe '#save' do
subject { described_class.new(attributes) }
let(:attributes) { title: 'Example', user: create(:user) }
it 'saves' do
expect(subject.save).to eq(true)
end
context 'when trying to set a disallowed title' do
let(:attributes) { super().merge(title: 'Hello') } # <==
it 'will not save' do
expect(subject.save).to eq(false)
end
end
end
I suggest you don't make a habit of using this regula...
Testing HTTPS with badssl.com
Website that offers lots of different kinds of HTTPS configurations, bad or good or complicated.
They also offer a dashboard to check if your browser's HTTPS handling works as expected (which might be compromised e.g. due to security products or enterprise proxy servers).
Recommended Git workflow for feature branches
This is a guide on how to effectively use Git when working on a feature branch. It is designed to get out of your way as much as possible while you work, and ensure you end up with clean commits in the end.
We assume you are the only person working on this branch. We also assume the branch has never been "partially" merged into master.
You want to start a feature branch
git checkout master
git checkout -b my-feature-branch
git push -u origin my-feature-branch
You've added code that works ind...
Integrating ESLint
Introduction
To ensure a consistent code style for JavaScript code, we use ESLint. The workflow is similar to integrating rubocop for Ruby code.
1. Adding the gem to an existing code base
You can add the following lines to your package.json
under devDependencies
:
"devDependencies": {
"eslint": "^8.7.0",
"eslint-config-standard": "^16.0.3",
"eslint-plugin-import": "^2.25.4",
"eslint-plugin-node"...
Git: rebase dependent feature branch after squash
This card will show you how to use git rebase --onto
without confusion.
Use case:
You've got two feature branches (one
and two
), where two
depends on one
. Now commits of branch one
have changed after you branched two
from it (i.e. after code review the commits of branch one
are squashed). Thus the commit history of branch one
has changed. Branch two
's history however didn't change.
Solution:
To make the branches share the same commit history again you will have to rebase and replay (attach) the a...
Jasmine: using async/await to write nice asynchronous specs
Jasmine has long standing support for writing asynchronous specs. In days gone by we used the done
callback to achieve this, but these days it is possible to write much more readable specs.
Async specs
As a first example, say we want to check that some form disables the submit button while working.
// bad (how we used to do it)
beforeEach(() => {
this.form = setupMyForm()
this.submitButton = findTheSubmitButton()
})
it('disables the submit button while working', (done) => {
expect(this.submitButton.disabled).toBe(false)
...
How to add esbuild to the rails asset pipeline
This are the steps I needed to do to add esbuild to an application that used the vanilla rails asset pipeline with sprockets before.
Preparations
- update Sprockets to version 4
- add a
.nvmrc
with your preferred node version (and install it) - add gems
jsbundling-rails
andforeman
to yourGemfile
:gem 'jsbundling-rails' group :development, :test do gem 'foreman' # ... end
bundle install
- run
bin/rails javascript:install:esbuild
in a console to prepare esbuild. - run
yarn install
...
Understanding Ruby's def keyword
This StackOverflow question about nested function definitions in Ruby imparts a good understanding of Ruby's def
.
PostgreSQL: "WHERE NOT <column> = '<value>'" statements do not include NULL values
Sometimes we write plain SQL queries in migrations so we don't have to mock ActiveRecord classes. These two migrations do the same:
class Migration1 < ActiveRecord::Migration[5.2]
class User < ActiveRecord::Base; end
def up
add_column :users, :trashed, :boolean
User.update_all(trashed: false)
end
end
class Migration2 < ActiveRecord::Migration[5.2]
def up
add_column :users, :trashed, :boolean
update("UPDATE users SET trashed = #{quoted_false}")
end
end
The plain SQL migration is less code, but h...
Regular Expressions: Excessive backtracking can get yourself in trouble
Two weeks ago, Cloudflare was struck by a global outage that lasted ~30 minutes. The incident was rooted on a CPU exhaustion caused by a single regular expression containing some catastrophic backtracking:
.*(?:.*=.*)
This is a small reminder do keep using the lazy operator ?
whenever possible and furthermore be aware that regular expressions should not only be unit-tested but also evaluated in terms of performance. See <https://makandracards.com/makandra/494822-regul...