Pattern: Disabling a certain feature in tests
There is a kind of features in web applications that hinder automated integration tests. Examples include cookie consent banners or form captchas. Clearly, these should be disabled so you do not have to explicitly deal with them in each and every test (like, every test starting with accepting the cookies notice). On the other hand, they must be tested as well.
A good feature disabling solution should therefore meet these requirements:
-
The feature is generally disabled in tests. A test does not need to do anything manually.
-
It is *...
Ruby: A small summary of what return, break and next means for blocks
Summary
- Use
return
to return from a method.return
accepts a value that will be the return value of the method call. - Use
break
to quit from a block and from the method that yielded to the block.break
accepts a value that supplies the result of the expression it is “breaking” out of. - Use
next
to skip the rest of the current iteration.next
accepts an argument that will be the result of that block iteration.
The following method will serve as an example in the details below:
def example
puts yield
puts ...
Defining custom RSpec matchers
There are three ways to define your own RSpec matchers, with increasing complexibility and options:
1) Use RSpec::Matchers.define
RSpec::Matchers.define :be_a_multiple_of do |expected|
match do |actual|
actual % expected == 0
end
# optional
failure_message do |actual|
"expected that #{actual} would be a multiple of #{expected}"
end
# optional
failure_message_when_negated do |actual|
"expected that #{actual} would not be a multiple of #{expected}"
end
end
- This is automatically available i...
Lazy-loading images
Note
This card does not reflect the current state of lazy loading technologies. The native lazy attribute could be used, which is supported by all major browsers since 2022.
Since images are magnitudes larger in file size than text (HTML, CSS, Javascript) is, loading the images of a large web page takes a significant amount of the total load time. When your internet connection is good, this is usually not an issue. However, users with limited bandwidth (i.e. on mobile) need to mine their data budget...
Preconnect, Prefetch, Prerender ...
A very informative and interesting presentation about browsing performance, looking at efforts Google Chrome takes to increase it.
From those slides
There is a bunch of interesting pages in Chrome:
- chrome://dns - List of prefetched DNS
- chrome://predictors/ - Chrome knows where you'll go
Preconnect
With <link rel="preconnect" href="https://the-domain.com">
in an HTML head, you give the browser an early hint that it will need to access the mentioned domain. By setting up the connection in advance, page load performance gets im...
RSpec: How to compare ISO 8601 time strings with milliseconds
Rails includes milliseconds in Time
/ DateTime
objects when rendering them as JSON:
JSON.parse(User.last.to_json)['created_at']
#=> "2001-01-01T00:00:00.000+00:00"
In RSpec you might want to use .to_json
instead of .iso8601
to use the build-in eq
matcher:
it 'returns the created at attribute of a user' do
get '/users/1'
expect(JSON.parse(response.body)['created_at']).to eq(Time.parse('2001-01-01').to_json)
end
Otherwise the strings do not match:
DateTime.parse('2001-01-01').to_s (will defa...
How to reliably center (block) icons vertically with text
vertical-align
is hard. Have you ever wanted to vertically center an icon with text? This usually means "vertically align with capital letters", as visually, a text line goes from baseline up to the capital top. (That's because descenders are far less frequent than ascenders.)
In this card we'll vertically center an icon (or any "blockish" inline element, really) with the capital letters of surrounding text. This works well with our [modern approach to SVG icons](/mak...
How to kill a Rails development server by force
Sometimes, the rails dev server doesn't terminate properly. This can for example happen when the dev server runs in a RubyMine terminal.
When this happens, the old dev server blocks port 3000, so when you try to start a new server, you get the error:
Address already in use - bind(2) for "127.0.0.1" port 3000 (Errno::EADDRINUSE)
You can terminate such a dev server with this command:
lsof -t -i :3000 -s TCP:LISTEN | xargs kill -9
It might be worth it to add this to your bash aliases.
Consul 1.3.0 lets you override generated controller methods
When you use the :as
option to map a power to a controller method you can now override the generated method. The original implementation can be accessed with super
.
This is useful to chain additional conditions to a scope:
class NotesController < ApplicationController
power :notes, as: :note_scope
# ...
private
def note_scope
super.where(trashed: false)
end
end
Dynamic super-overridable methods in Ruby – The Pug Automatic
How a macro can dynamically define a method that can be overridden with super
in the same class.
You can use the with_module_inheritance
helper below if you want. It can be handy to make parts of a modularity trait super
-able.
# ./lib/ext/module/with_module_inheritance.rb
#
# This macro allows you to define methods in a modularity trait that can be
# modified using the `super` keyword
# See https://thepugautomatic.com/2013/07/dsom/
module WithModuleInheritance
def with_module_inher...
Fixing wall of net/protocol warnings
After upgrading to Rails 6.1.7.2 one of our apps printed a wall of warnings while booting:
/var/www/app/shared/bundle/ruby/2.6.0/gems/net-protocol-0.2.1/lib/net/protocol.rb:68: warning: already initialized constant Net::ProtocRetryError
/home/deploy-app/.rbenv/versions/2.6.10/lib/ruby/2.6.0/net/protocol.rb:66: warning: previous definition of ProtocRetryError was here
/var/www/app/shared/bundle/ruby/2.6.0/gems/net-protocol-0.2.1/lib/net/protocol.rb:214: warning: already initialized constant Net::BufferedIO::BUFSIZE
/home/deploy-app/.rben...
Why Sidekiq Jobs should never be enqueued in an `after_create` or `after_save` callback
When an object is created / updated, various callbacks are executed in this order:
before_validation
after_validation
before_save
around_save
before_create
around_create
after_create
after_save
after_commit / after_rollback
Thus, each of these callbacks is executed at a specific time in the life cycle of the object. This is important because this point in time determ...
Defining class methods with Modularity traits
There are two ways to define a class method from a Modularity trait. Note that the usual caveats regarding class method visibility apply.
Using define_method
The recommended way is to define a method on your module's singleton class:
module SomeTrait
as_trait do
define_singleton_method :foo do
# ...
end
end
end
Using def (has...
Do not rescue inline in Ruby
When you are calling a method that may raise an exception that you don't care about, you might think of doing something like this:
@user = User.power_find(something) rescue User.new
Do not do that! You will be rescuing away StandardError
and all its subclasses, like NameError
-- meaning that even a typo in your code won't raise an error.
Instead, rescue the exception type that you are expecting:
@user = begin
User.power_find(something)...
Heads up: expect(object).to receive(:method_name) does not execute the original implementation of the method
Let's assume that we have a model Movie
that registers a callback function when a new instance of Movie
is created (Note: For the purpose of this card it is not important what that callback does or which type of callback it is).
This is how we test whether the callback function (here it is named :my_method
) is called when a new movie is created:
expect_any_instance_of(Movie).to receive(:my_method)
create(:movie) # <-- this is where the method :my_method should be called
You might expect that when calling `create(:mo...
How to make your git aliases work with both master and main
The linked article found a simple way to rewrite legacy git aliases to make them work with differently named default branches
- Step 1: Decide which is the most common default branch name of your projects, e.g.
master
. Define it as the globalinit.defaultBranch
git configuration :
git config --global init.defaultBranch master
- Step 2: Overwrite the value in each project directory that uses different defaults
# cd /path/to/project, then run:
git config ...
Spreewald: patiently blocks must not change variables from the surrounding scope
I recently enjoyed debugging a Cucumber step that tried to be retryable using a patiently
block:
Then /^"([^"]*)" should( not)? be selected for "([^"]*)"$/ do |value, negate, field|
patiently do
field = find(:label, text: field)['for'].delete_suffix('-ts-control')
...
end
end
Unfortunately this block is not retryable:
- The first attempt changes the value of
field
. - All subsequent attempts will using the changed value of
field
, instead of the o...
Signed URLs with Ruby on Rails
Using ActiveRecord's #signed_id
and .find_signed
methods you can create URLs that expire after some time. No conditionals or additional database columns required.
Finding a method name on a Ruby object
Wondering how a specific method on an object is exactly named? You can use Enumerable#grep
to detect it in the array of methods.
@user.methods.grep /name/ # => [:name, :first_name, :last_name]
You can also call #private_methods
or #public_methods
. To find only relevant methods, it is suggested to subtract generic methods like this:
User.methods - Object.methods
User.methods - ActiveRecord::Base.methods
@user.methods - Object.instance_methods
@user.methods - ActiveRecord::Base.instance_methods
RubyMine's clipboard can hold more than one string
By pressing Ctrl
+ Shift
+ V
you can select a recently copied string for pasting.
Ruby: Natural sort strings with Umlauts and other funny characters
Why string sorting sucks in vanilla Ruby
Ruby's sort
method doesn't work as expected with special characters (like German umlauts):
["Schwertner", "Schöler"].sort
# => ["Schwertner", "Schöler"] # you probably expected ["Schöler", "Schwertner"]
Also numbers in strings will be sorted character by character which you probably don't want:
["1", "2", "11"].sort
# => ["1", "11", "2"] # you probably expected ["1", "2", "11"]
Also the sorting is case sensitive:
...
Bundler 2.3 honors the version specified in `BUNDLED_WITH`
Bundler so far ignored the version specified under BUNDLED_WITH
in the Gemfile.lock
. This had two annoying consequences:
- If the bundler version on your system was lower than in the
Gemfile.lock
, you got an error message and had to manually install the correct version. - If the bundler version on your system was higher than in the
Gemfile.lock
, bundler silently updated the version in theGemfile.lock
to your system's bundler version. To avoid this, you had to always specify, which version you want to use for each bundler c...
Ruby object equality
TLDR
if you define a equality method for a class you must also implement
def hash
.
Ruby has a lot of methods that have to do something with equality, like ==
, ===
, eql?
, equal?
. This card should help you differentiate between those and give you hints on how to implement your own equality methods in a safe manner.
Differences between the methods
for everyday use: ==
When you compare two objects in ruby, you most often see the use of foo == bar
. By default the ==
operator inherits from Object
and is impl...
Fixing Yarn 1 error "unexpected end of file"
Our CI setup frequently sees this error while running yarn install
:
yarn install v1.22.19
[1/4] Resolving packages...
[2/4] Fetching packages...
error An unexpected error occurred: "https://registry.yarnpkg.com/typescript/-/typescript-3.9.7.tgz: unexpected end of file".
info If you think this is a bug, please open a bug report with the information provided in "/builds/projects/foo-app/yarn-error.log".
info Visit https://yarnpkg.com/en/docs/cli/install for documentation about this command.
This error is caused by [Yarn not retryin...