RSpec: automatic creation of VCR cassettes
This RailsCast demonstrated a very convenient method to activate VCR for a spec by simply tagging it with :vcr
.
For RSpec3 the code looks almost the same with a few minor changes. If you have the vcr
and webmock
gems installed, simply include:
# spec/support/vcr.rb
VCR.configure do |c|
c.cassette_library_dir = Rails.root.join("spec", "vcr")
c.hook_into :webmock
end
RSpec.configure do |c|
c.around(:each, :vcr) do |example|
name = example.metadata[:full_descripti...
Rails: Rest API post-mortem analysis
This is a personal post-mortem analysis of a project that was mainly build to provide a REST API to mobile clients.
For the API backend we used the following components:
- Active Model Serializer (AMS) to serializer our Active Record models to JSON.
- JSON Schema to test the responses of our server.
- SwaggerUI to document the API.
It worked
The concept worked really good. Here are two points that were extraordinary compared to normal Rails project with many UI components:
- Having a Rails application, that has no UI components (only...
How to simulate limited bandwidth in Google Chrome and Firefox
Your development machine is usually on a very good network connection.
To test how your application behaves on a slow network (e.g. mobile), you can simulate limited bandwidth.
Chrome
- Open the dev tools (Ctrl+Shift+I or F12) and switch to the "Network" tab
- In the row below the dev tool tabs, there's a throttling dropdown which reads "Online" by default.
- Inside the dropdown, you will find a few presets and an option to add your own download/upload/latency settings.
Firefox
- Open the dev tools (Ctrl+Shift+I or F12) and switc...
Install or update Chromedriver on Linux
Option 0: Download from the official page (preferred)
- Open https://googlechromelabs.github.io/chrome-for-testing/
- In Section "Stable" > chromedriver / linux64 > Download ZIP from URL
- Take the
chromedriver
binary from the ZIP file and put it e.g. into~/bin
.
Chromedriver must be available in your path. You can add ~/bin
to your path like this:
echo "export PATH=$PATH:$HOME/bin" >> $HOME/.bash_profile
If you're also using Geordi, disable automatic updating of chromedriver in ~/.config/geordi/global.yml
:
a...
What we know about PDFKit
What PDFKit is
- PDFKit converts a web page to a PDF document. It uses a Webkit engine under the hood.
- For you as a web developer this means you can keep using the technology you are familar with and don't need to learn LaTeX. All you need is a pretty print-stylesheet.
How to use it from your Rails application
- You can have PDFKit render a website by simply calling
PDFKit.new('http://google.com').to_file('google.pdf')
. You can then send the...
Why preloading associations "randomly" uses joined tables or multiple queries
ActiveRecord gives you the :include
option to load records and their associations in a fixed number of queries. This is called preloading or eager loading associations. By preloading associations you can prevent the n+1 query problem that slows down a many index view.
You might have noticed that using :include
randomly seems to do one of the following:
- Execute one query per involved table with a condit...
Taking screenshots in Capybara
Capybara-screenshot can automatically save screenshots and the HTML for failed Capybara tests in Cucumber, RSpec or Minitest.
Requires Capybara-Webkit, Selenium or poltergeist for making screenshots. They're saved into $APPLICATION_ROOT/tmp/capybara
The attached files contain config for cucumber integration and a Then show me a screenshot
step.
If your project uses Spreewald, you can use its Then show me the page
step instead.
Inclu...
Capybara: Pretending to interact with the document
Browsers blocks abusable JavaScript API calls until the user has interacted with the document. Examples would be opening new tab or start playing video or audio.
E.g. if you attempt to call video.play()
in a test, the call will reject with a message like this:
NotAllowedError: play() failed because the user didn't interact with the document first. https://goo.gl/xX8pDD
Workaround
To pretend document interaction in a test you can create an element, click on it, and remove the element again. This unblocks the entire JavaSc...
How to set up database_cleaner for Rails with Cucumber and RSpec
Add gem 'database_cleaner'
to your Gemfile. Then:
Cucumber & Rails 3+
# features/support/database_cleaner.rb
DatabaseCleaner.clean_with(:deletion) # clean once, now
DatabaseCleaner.strategy = :transaction
Cucumber::Rails::Database.javascript_strategy = :deletion
Cucumber & Rails 2
The latest available cucumber-rails
for Rails 2 automatically uses database_cleaner
when cucumber/rails/active_record
is required -- but only if transactional fixtures are off. To have database_cleaner
work correctly:
- Add the at...
Simple database lock for MySQL
Note: For PostgreSQL you should use advisory locks. For MySQL we still recommend the solution in this card.
If you need to synchronize multiple rails processes, you need some shared resource that can be used as a mutex. One option is to simply use your existing (MySQL) database.
The attached code provides a database-based model level mutex for MySQL. You use it by simply calling
Lock.acquire('string to synchronize on') do
# non-th...
Rails: Example on how to extract domain independent code from the `app/models` folder to the `lib/` folder
This cards describes an example with a Github Client on how to keep your Rails application more maintainable by extracting domain independent code from the app/models
folder to the lib/
folder. The approach is applicable to arbitrary scenarios and not limited to API clients.
Example
Let's say we have a Rails application that synchronizes its users with the Github API:
.
└── app
└── models
├── user
│ ├── github_client.rb
│ └── sychronizer.rb
└── user.rb
In this example the app folder ...
An auto-mapper for ARIA labels and BEM classes in Cucumber selectors
Spreewald comes with a selector_for
helper that matches an English term like the user's profile
into a CSS selector. This is useful for steps that refer to a particular section of the page, like the following:
Then I should see "Bruce" within the user's profile
^^^^^^^^^^^^^^^^^^
If you're too lazy to manually translate English to a CSS selector by adding a line to features/env/selectors.rb
, we already have an [auto-mapper to translate English into ...
How to: Upgrade CarrierWave to 3.x
While upgrading CarrierWave from version 0.11.x to 3.x, we encountered some very nasty fails. Below are the basic changes you need to perform and some behavior you may eventually run into when upgrading your application. This aims to save you some time understanding what happens under the hood to possibly discover problems faster as digging deeply into CarrierWave code is very fun...
Whitelists and blacklists
The following focuses on extension allowlisting, but it is the exact same thing for content type allowlisting with the `content_ty...
Switch to a recently opened tab with Cucumber
Similar to closing an opened browser window, spreewald now supports the I switch to the new browser tab
step.
Info
See the Spreewald README for more cool features.
You can use it to test links that were opened with a link_to(..., :target => '_blank')
link or other ways that create new tabs or windows.
Important
This only works with
Selenium
...
RSpec: Executing specs by example id (or "nesting index")
There are several ways to run a single spec. I usually copy the spec file path with the line number of the example and pass it to the RSpec binary: bin/rspec spec/models/user_spec.rb:30
(multiple line numbers work as well: :30:36:68
). Another is to tag the example with focus: true
or to run the example by matching its name.
In this card I'd like to ...
RestClient / Net::HTTP: How to communicate with self-signed or misconfigured HTTPS endpoints
Occasionally, you have to talk to APIs via HTTPS that use a custom certificate or a misconfigured certificate chain (like missing an intermediate certificate).
Using RestClient will then raise RestClient::SSLCertificateNotVerified
errors, or when using plain Net::HTTP:
OpenSSL::SSL::SSLError: SSL_connect returned=1 errno=0 state=SSLv3 read server certificate B: certificate verify failed
Here is how to fix that in your application.
Important: Do not disable certificate checks for production. The interwebs are full of people say...
Cucumber step to match table rows with Capybara
These steps are now part of Spreewald.
This note describes a Cucumber step that lets you write this:
Then I should see a table with the following rows:
| Bruce Wayne | Employee | 1972 |
| Harleen Quinzel | HR | 1982 |
| Alfred Pennyworth | Engineering | 1943 |
If there are additional columns or rows in the table that are not explicitely expected, the step won't complain. It does however expect the rows to be ordered as stat...
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...
Geordi 6.0.0 released
6.0.0 2021-06-02
Compatible changes
-
geordi commit
will continue even if one of the given projects is inaccessible. It will only fail if no stories could be found at all.
Breaking changes
- Removed VNC test browser support for integration tests – Headless Chrome has
matured and is almost a drop-in replacement. Also, key binding issues have
increased with VNC and recent Linux.- Please use a headless Chrome setup https://makandracards.com/makandra/492109-capybara-running-tests-with-headless-chrome.
- You might also ...
OpenAI TTS: How to generate audio samples with more than 4096 characters
OpenAI is currently limiting the Audio generating API endpoint to text bodies with a maximum of 4096 characters.
You can work around that limit by splitting the text into smaller fragments and stitch together the resulting mp3 files with a CLI tool like mp3wrap or ffmpeg.
Example Ruby Implementation
Usage
input_text = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Mi eget mauris pharetra et ultrices neque."
output_mp3_path = Rails.root.join("tts/ipsum...
RSpec's hash_including matcher does not support nesting
You can not use the hash_including
argument matcher with a nested hash:
describe 'user' do
let(:user) { {id: 1, name: 'Foo', thread: {id: 1, title: 'Bar'} }
it do
expect(user).to match(
hash_including(
id: 1, thread: {id: 1}
)
)
end
end
The example will fail and returns a not very helpful error message:
expected {:id => 1, :name => "Foo", :thread => {:id => 1, :title => "Bar"}} to...
Bash script to list git commits by Linear ID
As we're switching from PT to Linear, I've updated the existing bash script to work for commits that are referencing Linear IDs.
A core benefit of our convention to prefix commits by their corresponding issue ID is that we can easily detect commits that belong to the same issue. You can either do that manually or use the bash script below. It can either be placed in your .bashrc
or a...
How to open files from better_errors with RubyMine on Linux
I recently noticed that better_errors
allows you to to open files from within your favorite editor. However it was not so easy to get rubymine://
links to work on Gnome/Linux. Here is how it finally worked for me:
Step 1: Add a Desktop launcher
Add this file to ~/.local/share/applications/rubymine.desktop
:
[Desktop Entry]
Version=1.0
T...
ActiveType::Object: Be careful when overriding the initialize method
Background:
ActiveType::Object
inherits from ActiveRecod::Base
and is designed to behave like an ActiveRecord Object, just without the database persistence.
Don't remove any of the default behavior of the initialize method!
If you have a class which inherits from ActiveType::Object
and you need to override the #initialize
method, then you should be really careful:
- Always pass exactly one attribute.
ActiveRecod::Base
objects really want to get their arguments processable as keyword arguments. Don't change the syntax, or y...