Ruby: Reading and writing CSVs

In ruby you can easily read and write CSVs with the standard CSV library class.

On top of this, you can use the gem smarter_csv for reading (not writing) CSVs in a more comfortable way:

  • Keep in mind, that the development of this gem is in an unknown state and the 2.0 release seems to happen never
  • The API will change completely for 2.0, so you might find a bunch of unrelated documentation for 1.2

Here is an example...

Cucumber: How to find unused step definitions

Cucumber has an output format that prints step definitions only. You can use this to find unused ones:

  1. Temporarily add require_relative 'env' to the top of the first file in features/support. --dry-run makes Cucumber skip loading env.rb.
  2. Open a really wide terminal window.
  3. bundle exec cucumber --dry-run --format stepdefs | grep -B1 'NOT MATCHED' --no-group-separator | grep features/step_definitions

This will print all unused step definitions from your project – however, the result will include false positives. Step...

How to upgrade Rails: Workflow advice

When upgrading Rails versions -- especially major versions -- you will run into a lot of unique issues, depending on the exact version, and depending on your app.

However, it is still possible to give some generic advice on how you want to tackle the update in principle.

If you are not really confident about upgrading Rails, have a look at Rails LTS.

How many update steps?

Besides the Rails upgrade itself, you might also want to upgrade your other gems and upgrade your Ruby version.
First decide in how many st...

Regular tasks for long-running projects

When projects run for many years, they require special regular maintenance to stay fresh. This kind of maintenance is usually not necessary for small or quick projects, but required to keep long-running projects from getting stale.

You should be able to fit this into a regular development block.

Quarterly

Check which libraries need updating

As time goes by, libraries outdate. Check your software components and decide if any of it needs an update. Your main components (e.g. Ruby, Rails, Unpoly) should always be reasonably up to da...

Exception notifier: How to provide custom data to fail mails

The exception_notification gem supports to provide custom data to e.g. the fail mail within foreground or background jobs.

ExceptionNotifier.notify_exception(_ex_, :data => {:message => "was doing something wrong"})

Still this can be blocked if you have an initializer where you override the default sections and background_sections option. So remember to add the data option to the desired section if required. In case you raise an exception without a data object, the fail...

Katapult 0.5.0 released

New Features

  • Deployment ready for Opscomplete
  • Copying view and controller templates over to target application during
    basics configuration or via new command katapult templates.
  • "Usage" section in README rewritten: Now describes two usage scenarios.

Improvements

  • Generating a fixed Gemfile.lock. Run bundle update after code generation to
    update all gems to recent versions.
  • Better deployment with Webpack
  • Navigation only rendered if requested
  • Some minor fixes

RubyMine: How to add a german spell checker

Grazie Lite

Rubymine 2024.3 bundles Grazie Lite by default. You need to enabled "German" under Settings/Preferences | Editor | Natural Languages.

Hunspell (legacy)

  1. Install the Hunspell plugin and restart Ruby Mine
  2. Run sudo apt install hunspell-de-de
  3. Select /usr/share/hunspell/de_DE.dic in File > Settings > Editor > Spelling > Custom Directory +

![Screenshot_from_2018-10-17_16-47-03.png](https://makandracards.com/makandra/57341-rubymine...

How to: Run geordi in a single proccess with parallel test setup

Geordi uses parallel_tests if available for running the test suite. To debug an application it is very unhandy to have multiple processes as your terminal I/O will not work as expected once a breakpoint is hit.

Even parallel_tests support an option to enable a single process run, it is not possible to pass this option through geordi. But you can set the number of processes via ENV variable manually:

PARALLEL_TEST_PROCESSORS=1 bundle exec geordi cucu...

Accessing Rails config in webpack(er)

It is possible to access Rails config (for example secrets) from within your webpack bundles, thanks to rails-erb-loader. When using webpacker, the setup is like this:

  1. Install rails-erb-loader:

    yarn add rails-erb-loader
    
  2. Add this to your config/webpacker/environment.js:

    environment.loaders.prepend('erb', {
      test: /\.erb$/,
      enforce: 'pre',
      use: [{
        loader: 'rails-erb-loader',
      }]
    })
    
  3. Start using erb. For examp...

How to examine an unknown Ruby object

When debugging your application, you will come across objects created by some gem or framework. You don't have the source code at hand, still need to inspect this object. Here are some tools to do so:

Relevant methods

@object.methods - Object.instance_methods returns a list of methods excluding methods inherited from Object. This makes the methods list drastically more relevant. You can also try subtracting other base classes like ActiveRecord::Base.methods etc.
To further narrow it down you can also just look at public methods...

Rbenv: How to remove a gem installed from a Github source

Normally you can list all gems of the current ruby version with gem list, which also includes the gems of you Gemfile. These can be uninstalled with gem uninstall gemname.

List and uninstall a gem installed via Bundler from Github

This does not work for gems installed directly from Github. They do not appear in gem list.

Show all gems installed via Github by bundler:

ls ~/.rbenv/versions/2.4.1/lib/ruby/gems/2.4.0/bundler/

Remove a gem installed via Github by Bundler:

rm -rf ~/.rbenv/versions/2.4.1/lib/ruby/gems/2....

How to let passenger restart after deployment with capistrano

Phusion Passenger changed the way how it gets restarted several times. Through the project's history, these all were valid:

  • touch tmp/restart.txt
  • sudo passenger-config restart-app /path/to/app
  • passenger-config restart-app /path/to/app

You should not need to know which one to use. Instead, the capistrano-passenger gem will choose the appropriate restart mechanism automatically based on your installed the passenger version.

Installation

  1. Add to your Gemfile:

    gem 'capistr...
    

An incomplete guide to migrate a Rails application from paperclip to carrierwave

In this example we assume that not only the storage gem changes but also the file structure on disc.

A general approach

Part A: Create a commit which includes a script that allows you to copy the existing file to the new file structure.

Part B: Create a commit which removes all paperclip logic and replace it with the same code you used in the first commit

Part A

Here are some implementation details you might want to reuse:

  • Use the existing models to read the files from
  • Use your own carrierwave models to write t...

Geordi: How to rerun failed features

Geordi's cucumber command has a --rerun option that reruns failing tests the given number of times. Usage:

geordi cucumber path/to/features --rerun=2
geordi cucumber path/to/features -r2

Background and how to rerun manually

Cucumber will save a file tmp/parallel_cucumber_failures.log containing the filenames and line number of the failed scenarios after a full test run. Normally you can say cucumber -p rerun (rerun is a profile defined by default in config/cucumber.yml) to rerun all failed scenarios.

Here are a few al...

Rails < 5: How to get after_commit callbacks fired in tests

If you use transactional_fixtures or the database_cleaner gem with strategy :transaction, after_commit callbacks will not be fired in your tests.

Rails 5+

Rails 5 has a fix for this issue and no further action is needed.

Rails 3, Rails 4

Add the gem test_after_commit to your test group in the Gemfile and you are done. You don't need to change the database strategy to deletion (wh...

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.

  1. Add a lib/ext/super_client.rb to your project (see How to organize monkey patches in Ruby on Rails projects)
  2. Add the extension, which ov...

How to write a good changelog

We want to keep a changelog for all gems we maintain. There are some good practices for writing a changelog that adds value, please stick to these.

  • Add a notice to the README's contribute section about the changelog
  • For every release update the changelog
  • Note the date format yyyy-mm-tt

What is a changelog?

A changelog is a file which contains a curated, chronologically ordered list of notable changes for each version of a project.

Why keep a changelog?

To make it easier for users and...

How to make changes to a Ruby gem (as a Rails developer)

At makandra, we've built a few gems over the years. Some of these are quite popular: spreewald (> 1M downloads), active_type (> 1M downloads), and geordi (> 200k downloads)

Developing a Ruby gem is different from developing Rails applications, with the biggest difference: there is no Rails. This means:

  • no defined structure (neither for code nor directories)
  • no autoloading of classes, i.e. you need to require all files yourself
  • no active_support niceties

Also, their scope...

How to fill in multiple lines in a textarea with cucumber

If you want to fill in textareas with multiple lines of text (containing line breaks / new lines) you can use Cucumber's docstrings:

And I fill in "Comment" with:
  """
  This is a long comment.
  With multiple lines.

  And paragraphs.
  """

The step definition is part of the spreewald gem

Whenever: Don't forget leading zeros for hours!

Whenever is a Ruby gem that provides a nicer syntax for writing and deploying cron jobs.

Leading zeros are important for whenever if you use the 24-hours format!

This schedule.rb:

every 1.day, at: '3:00', roles: [:primary_cron] do
  runner 'Scheduler.delay.do_things'
end

will lead to this crontab entry (crontab -l) with the default configuration:

0 15 * * * /bin/bash -l -c 'cd /var/www/my-project/releases/20180607182518 && bin/rails runner -e production '\''Scheduler.delay.do_things'\'''

Which would run on 3...

Capistrano + Rails: Tagging production deploys

Just like Ruby Gems tag their version releases to the corresponding Git commit, it can be helpful to track production deploys within the commit history. This task does the tagging for you.

Capistrano 3

# lib/capistrano/tasks/deploy.rb
namespace :deploy do
  ...

  desc 'Tag the deployed revision'
  task :tag_revision do
    date = Date.today.to_s

    puts `git tag deploy-#{date} #{fetch :current_revision}`
    puts `git push --tags origin`
  end

end
# config/deploy/production.rb
after 'deploy:finished', 'deploy:tag_...

Unpoly: Loading large libraries on-demand

When your JavaScript bundle is so massive that you cannot load it all up front, I would recommend to load large libraries from the compilers that need it.

Compilers are also a good place to track whether the library has been loaded before. Note that including same <script> tag more than once will cause the browser to fetch and execute the script more than once. This can lead to memory leaks or cause duplicate event handlers being registered.

In our work we mostly load all JavaScript up front, since our bundles are small enough. We recent...

Webpacker: Configuring browser compatibility

Webpacker uses Babel and Webpack to transpile modern JavaScript down to EcmaScript 5. Depending on what browser a project needs to support, the final Webpack output needs to be different. E.g. when we need to support IE11 we can rely on fewer JavaScript features. Hence our output will be more verbose than when we only need support modern browsers.

Rails 5.1+ projects often use Webpacker to preconfigure the Webpack pipeline for us. The default configuration works something like this:

  1. Webpack checks w...

Bundler error: Downloading gem revealed dependencies not in the API

Recent Bundler (1.16.1) started complaining about missing dependencies in the Gemfile. This is due to a stricter handling of specifications (see attached link).

The error message looks like this:

Downloading example-gem-1.2.3 revealed dependencies not in the API or the lockfile (other-gem (< 3)).
Either installing with `--full-index` or running `bundle update example-gem` should fix the problem.

However, bundle install --full-index did not any better for me, and bundle update is not always a viable solution.

Easiest solut...