Test a download's filename with Cucumber
These steps are now part of Spreewald.
The step definitions below allow you to test the filename suggested by the server:
When I follow "Export as ZIP"
Then I should get a download with the filename "contacts_20110203.zip"
Capybara
Then /^I should get a download with the filename "([^\"]*)"$/ do |filename|
page.driver.response.headers['Content-Disposition'].should include("filename=\"#{filename}\"")
end
Webrat
Then /...
Webpack(er): A primer
webpack is a very powerful asset bundler written in node.js to bundle (ES6) JavaScript modules, stylesheets, images, and other assets for consumption in browsers.
Webpacker is a wrapper around webpack that handles integration with Rails.
This is a short introduction.
Installation
If you haven't already, you need to install node.js and Yarn.
Then, put
gem 'webpacker', '~> 4.x' # check if 4.x is still cu...
Guide to localizing a Rails application
Localizing a non-trivial application can be a huge undertaking. This card will give you an overview over the many components that are affected.
When you are asked to give an estimate for the effort involved, go through the list below and check which points are covered by your requirements. Work with a developer who has done a full-app localization before and assign an hour estimate to each of these points.
Static text
- Static strings and template text in
app
must be translated: Screens, mailer templates, PDF templates, helpe...
Test your CSS rendering output with GreenOnion
No one wants to cry over regression issues in views; does testing HTML and CSS have to be such a back and forth between designers and devs? Why is it that the rest of the stack can have TDD and BDD but not the presentation layer? Well, GreenOnion is here to help you get the same results on testing front-end styling that you've enjoyed in your unit and integration tests up to now.
GreenOnion records 'skins', which are snapshots of the current state of a view (or any page that a browser can navigate to). The first time that it is run on a view...
RSpec: How to aggregate failures
RSpec >= 3.3 added aggregate_failures, which allows multiple failures in an example and list them all, rather than aborting on the first failure.
This can be used:
- In the global configuration
- With the tag
:aggregate_failures
(our preferred option in case every expectations should be aggregated) - With the method
aggregate_failures
[Here](https://web.archive.org/web/20210110131654/https://relishapp.com/rspec...
Heads up: Sidekiq per default silently fails when retries are exhausted!
For Sidekiq to be able to retry your jobs it has to be able to catch errors that occur while a job is executed.
Per default, Sidekiq will not raise / notify you if the retry count is exhausted. It will only copy the job to the dead queue (see wiki).
If you want to get notified, you have to implement it in your worker explicitly with a sidekiq_retries_exhausted
-block, e.g. like this:
class DownloadWorker
include Sidekiq::Worker
# Import jobs are retried a few time...
Automatically validating dependency licenses with License Finder
"Open-source software (OSS) is great. Anyone can use virtually any open-source code in their projects."
Well, it depends. Licenses can make things difficult, especially when you are developing closed-source software. Since some OSS licenses even require the employing application to be open-sourced as well (looking at you, GPL), you cannot use such software in a closed-source project.
To be sure on this, we have developed a project-level integration of Pivotal's excellent [license_finder](https:/...
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 ...
Project management best practices: Technical debt summary
Maintaining larger projects makes it more difficult to balance refactoring and upgrade tasks according to its actual value. Consider to create and periodically maintain a summary, which helps you and your team in the decision which refactoring task should be taken next.
Template
Here is an template on how you might categorize your tasks:
| Technical debt | Estimated Efforts | Customer value| Customer value explained| Developer value|Developer value explained|
|-----------------------------|----------------|-----------|------...
Don't build randomness into your factories
Tests are about 100% control over UI interaction and your test scenario. Randomness makes writing tests hard. You will also push tests that are green for you today, but red for a colleague tomorrow.
That said, please don't do something like this:
Factory(:document) do |document|
document.category { ['foo', 'bar', 'baz'].sample }
end
Instead do this:
Factory(:document) do |document|
document.category 'foo'
end
The case against Faker
I even recommend to not use libraries like [Faker]...
Ruby: How to use prepend for cleaner monkey patches
Let's say you have a gem which has the following module:
module SuperClient
def self.foo
'Foo'
end
def bar
'Bar'
end
end
For reasons you need to override foo
and bar
.
Keep in mind: Your code quality is getting worse with with each prepend
(other developers are not happy to find many library extensions). Try to avoid it if possible.
- Add a
lib/ext/super_client.rb
to your project (see How to organize monkey patches in Ruby on Rails projects) - Add the extension, which ov...
RSpec: Expecting non-primitive objects as method invocation arguments
Expecting a primitive value as an argument to a method invocation is easy:
expect(object).to receive(:foo).with('arg1', 'arg2')
This expectation would be met with this call:
object.foo('arg1', 'arg2')
But what if the argument is expected to be an object with a given set of methods? E.g. this class with #first_name
and #last_name
methods:
class Person
def initialize(first_name, last_name)
@first_name = first_name
@last_name = last_name
end
attr_reader :first_name, :last_name
end
``...
Accessing JavaScript objects from Capybara/Selenium
When testing JavaScript functionality in Selenium (E2E), you may need to access a class or function inside of a evaluate_script
block in one of your steps. Capybara may only access definitions that are attached to the browser (over the window
object that acts as the base). That means that once you are exporting your definition(s) in Webpacker, these won't be available in your tests (and neither in the dev console). The following principles/concepts also apply to Sprockets.
Say we have a StreetMap
class:
// street_map.js
class S...
ActiveRecord: count vs size vs length on associations
TL;DR: You should generally use #size
to count associated records.
size
- Counts already loaded elements
- If the association is not loaded, falls back to a
COUNT
query
count
- If a counter cache is set up, returns the cached value
- Issues a
COUNT
query else
Caveats
- If you trigger a
COUNT
query for an association of an an unsaved record, Rails will try to load all children where the foreign keyIS NULL
. This is not what you want. To prevent this behavior, you can useunsaved_record.association.to_a.size
. - `c...
How Haml 6 changes attribute rendering, and what to do about it
Haml 6 was a major rewrite with performance in mind. To achieve a performance improvement of 1.7x, some design trade-offs had to be made. The most notable change might be the simplified attribute rendering.
In Haml 5, attribute rendering knew two special cases: an attribute with value true
would be rendered without a value, an attribute with a falsy value would not be rendered at all. All other values would just be rendered as attribute values.
According to the Haml maintai...
Upgrading Rails 2 from 2.3.8 through 2.3.18 to Rails LTS
This card shows how to upgrade a Rails 2 application from Rails 2.3.8 through every single patch level up to 2.3.18, and then, hopefully, Rails LTS.
2.3.8 to 2.3.9
This release has many minor changes and fixes to prepare your application for Rails 3.
Step-by-step upgrade instructions:
- Upgrade
rails
gem - Change your
environment.rb
so it saysRAILS_GEM_VERSION = '2.3.9'
- Change your ...
How to not repeat yourself in Cucumber scenarios
It is good programming practice to Don't Repeat Yourself (or DRY). In Ruby on Rails we keep our code DRY by sharing behavior by using inheritance, modules, traits or partials.
When you reuse behavior you want to reuse tests as well. You are probably already reusing examples in unit tests. Unfortunately it is much harder to reuse code when writing integration tests with Cucumber, where you need to...
Capybara: Working with invisible elements
When Capybara locates elements in the DOM, by default it allows only accessing visible elements -- when you are using a driver that supports it (e.g. Selenium, not the default Rack::Test
driver).
Consider the following HTML:
<div class="test1">One<div>
<div class="test2">Two</div>
With some CSS:
.test1 { display: block }
.test2 { display: none }
We will be using Capybara's find
below, but this applies to any Capybara finder methods.
Default: visible: :visible
As described above, by default Capybara finds ...
A different testing approach with Minitest and Fixtures
Slow test suites are a major pain point in projects, often due to RSpec
and FactoryBot
. Although minitest
and fixtures are sometimes viewed as outdated, they can greatly improve test speed.
We adopted a project using minitest and fixtures, and while it required some initial refactoring and establishing good practices, the faster test suite was well worth it! Stick with me to explore how these tools might actually be a good practice.
So, why is this setup faster? Partially, it's because minitest is more lightweight than RSpec
, which...
Using ngrok for exposing your development server to the internet
Sometimes you need to access a dev server running on localhost from another machine that is not part of the same network. Maybe you want to use your phone to test a web page, but are only in a guest WiFi. In the past, we often used some port forwarding or other techniques to expose the service to the internet.
Enter ngrok, a command line tool that gives you an on-the-fly internet...
Async control flow in JavaScript: Promises, Microtasks, async/await
Slides for Henning's talk on Sep 21st 2017.
Understanding sync vs. async control flow
Talking to synchronous (or "blocking") API
print('script start')
html = get('/foo')
print(html)
print('script end')
Script outputs 'script start'
, (long delay), '<html>...</html>'
, 'script end'
.
Talking to asynchronous (or "evented") API
print('script start')
get('foo', done: function(html) {
print(html)
})
print('script end')
Script outputs 'script start'
, 'script end'
, (long ...
How to list updateable dependencies with Bundler and Yarn
Bundler
bundle outdated [--filter-major|--filter-minor|--filter-patch]
Example output for bundle outdated --filter-major
Other examples
A useful flag is --strict
as it will only list versions that are allowed by your Gemfile requirements (e.g. does not show rails update to 6 if your Gemfile has the line gem 'rails', '~>5.2'
).
I also experienced that doing upgrades per group (test, development) are easier to do. Thus --groups
might also be helpful.
$ bundle...
Code splitting in esbuild: Caveats and setup
Code splitting is a feature of esbuild that can keep huge libraries out of the main bundle.
How code splitting works
Like Webpack esbuild lets you use the await import()
function to load code on demand:
// application.js
const { fun } = await import('library.js')
fun()
However, esbuild's code splitting is disabled by default. The code above would simply inline (copy) `l...
HTML: Making browsers wrap long words
By default, browsers will not wrap text at syllable boundaries. Text is wrapped at word boundaries only.
This card explains some options to make browsers wrap inside a long word like "Donaudampfschifffahrt"
.
Option 1: hyphens CSS property (preferred)
Modern browsers can hyphenate natively. Use the hyphens CSS property:
hyphens: auto
There is also hyphens: none
(disable hyphenations even at ­
entities) and hyphens: manual
(hy...