Toxic Elephant

Don't bury it in your back yard!

Some development automation

Posted by matijs 10/10/2020 at 14h32

For a long time, part of my weekend routine has been updating the dependencies of all my open source Ruby projects. I had set up some tooling to automatically run bundle outdated on all my project directories and make a nice report. For good measure, it would also run RuboCop and tell me if any work was needed on that front.

I would then go through the list of needed work, adjust the dependencies (using KeepUp where possible), activate new RuboCop cops, fix new RuboCop offenses, create pull requests, wait for builds to be done and then merge. There actually was a certain satisfaction in keeping things up-to-date, keeping things neat.

A few weeks ago, I’d had enough. The process of keeping things up-to-date was starting to become tedious, and it was keeping me from writing actual new software. Having had good experience at work with Dependabot I decided to automate dependency updates for all my open source repo’s.

After some experimenting I made the following changes to my repositories:

  • I added a separate named RuboCop job as part of each repository’s Travis CI configuration. To do this requires using the jobs key instead of rvm, like so:

        - rvm: 2.5
        - rvm: 2.6
        - rvm: 2.7
        - rvm: 2.7
          name: "RuboCop"
          script: bundle exec rubocop
  • I configured GitHub’s native version of Dependabot to create pull requests daily, using a file .github/dependabot.yml in each repository:

    version: 2
    - package-ecosystem: bundler
      directory: "/"
        interval: daily
        time: "04:23"
      open-pull-requests-limit: 10

All this means is that the manual part has been reduced to just checking that the builds are green for the pull requests produced by Dependabot, and potentially any new issues found by newer versions of RuboCop.

no comments no trackbacks


Posted by matijs 29/03/2020 at 08h38

Actually, problems only get solved because people roll up their sleeves and do shit, and government is the collective coordinating apparatus that helps us know what shit needs to get done and who needs to do it.

Current Affairs, Everything has changed overnight, via

no comments no trackbacks


Posted by matijs 20/03/2020 at 13h08

Automating away your library release process because you find it boring and tedious is the worst thing you can do. People rely on your releases to be meaningful, have meaningful version numbers, and meaningful release notes. Yes, these take time. But your releases are when your users are reminded that you exist. At other times, your library is just quietly doing its thing. Releases are when your users take notice. They want to read your change log, look at the version number to see if they need to pay attention. You’re in the spotlight. This is your performance. Give your releases some love.

Tags , no comments no trackbacks

Opinions about Ruby gem development, 2018 edition

Posted by matijs 31/12/2018 at 15h46

  • Your next release should nearly always come from the master branch.
  • When updating your feature branch, prefer git rebase master over git merge master.
  • When merging your feature into master, prefer merge bubbles over squash merges and fast-forwards.
  • bundle exec rake should run your tests.
  • You still should not check in Gemfile.lock.
  • Use RuboCop. Running just rubocop should do the right thing. If you need a specific version, add it to the Gemfile. In that case, bundle exec rubocop should do the right thing.

Tags , , no comments no trackbacks


Posted by matijs 20/09/2018 at 09h03

I happened upon this comment.

But more important, it just doesn’t work sensibly to explain why many people decline modest bets (e.g. that someone not on the brink of starvation would decline a 50/50 lose $100 vs gain $110) bet.

You can look at this bet in two ways. The first is the single bet. Then, you can think about how bad you feel about losing $100, versus how good you feel about gaining $110.

The second way is as a repeated bet. And I think this is how people do think about it: If I bet yesterday, why not bet today? Or, I lost yesterday, I need to bet again today to ‘make up for it’.

Emotions aside, the reason given that the bet is a good one, is that in the long run the better will come out ahead. But how long is the long run?

Let’s fire up irb. (I’ve reformatted the lines a bit to fit in an article layout.)

>> def bet; rand < 0.5 ? -100 : 110; end
>> count = 0; sum = 0; while sum < 1; count+= 1; sum += bet; end; [count, sum]
=> [81, 90] # Oops!
>> min = 0; count = 0; sum = 0; \
 >  while sum < 1; count+= 1; sum += bet; min = sum if sum < min; end; \
 >  [count, min, sum]
=> [35, -530, 70] # OOPS!

Maybe you can spare $100, but can you spare $530? (Not to mention the fact that many people can’t spare $100.).

Or even $1340, leading to a $50 win after 136 bets?

=> [136, -1340, 50]

What are the chances of a repeated bet ruining you before you gain anything at all?

>> def compound_bet; min = 0; count = 0; sum = 0; \
 >   while sum < 1; count+= 1; sum += bet; min = sum if sum < min; end; \
 >   [count, min, sum]; end
>> def killer_bet(threshold); count, min, sum = compound_bet; min < -threshold; end
>> def killer_chance(threshold); { killer_bet(threshold) }.count / 1000.0; end
>> killer_chance(500) #=> 8.017
>> killer_chance(1000) #=> 3.532

A betting scheme with a 3.5% chance of losing $1000 doesn’t sound so good…

(The commenter goes on to point to an article that actually doesn’t make the claim that the given debt is a ‘modest debt’, and seems far more interesting than that.)

Tags no comments no trackbacks

No-one understands SemVer

Posted by matijs 25/07/2018 at 06h59

I started reading this, and came upon this line:

Many people claim to know how SemVer works, but have never read the specification.

And I thought: Yes! This is exactly the problem. Everyone talks about SemVer, but no-one reads the specification, so the discussions don’t make sense. Finally, someone is going to Make Things Clear!

And then I read this:

Note: Stop trying to justify your refactoring with the “public but internal” argument. If the language spec says it’s public, it’s public. Your intentions have nothing to do with it.

What!? This person complains about people not reading the specifications, and then proceeds to contradict the very first article of the SemVer specification? Here it is (highlight mine):

Software using Semantic Versioning MUST declare a public API. This API could be declared in the code itself or exist strictly in documentation. However it is done, it should be precise and comprehensive.

Whether the language spec says it’s public has little to do with it.

Now, there’s a discussion going on on Hacker News about this article, and clearly I’m not the only one bothered by the quote above, but the commenters are focused on whether languages allow you to control what part of your API is exposed, rather than what the SemVer spec actually says.

No-one understands SemVer.

Tags , , no comments no trackbacks

Importing GTG tasks into Taskwarrior

Posted by matijs 06/06/2018 at 12h47

I used to use Getting Things Gnome (GTG) to keep my TODO list. However, the project seems dead right in the middle of its Gtk+ 3.0 port, so I’ve been looking around for an alternative. After much consideration, I decided on Taskwarrior. I wanted to keep my old tasks and couldn’t find a nice way to export them from GTG, let alone import them into Taskwarrior. So in the end I decided to create my own exporter.

Getting Things Gnome keeps your tasks in some simple XML files in a known location. HappyMapper is ideal for this. I started out using its automatic mapping, but as my understanding of the GTG format deepened, I switched to explicit mapping of a Task’s attributes and elements.

On the other side, Taskwarrior can import simple JSON files that are super easy to create using JSON from the standard library. The script below will output this format to STDOUT. It’s up to you to use task import to process it further.

I implemented this as a spike, so there are no tests, but I like to think the design I ended up with is quite testable. I get annoyed whenever code becomes cluttered, or top-level instance variables start to appear. So I tend to quickly split off classes that have a distinct responsibility. I may yet convert this to a real gem and see how easy it is to bring everything under test.

Finally, before showing the code, I should warn you that it’s probably a good idea to back up your existing Taskwarrior data before playing with this.

Here’s the code:

#!/usr/bin/env ruby

require happymapper require json

class Task include HappyMapper

attribute :id, String attribute :status, String attribute :tags, String attribute :uuid, String

element :title, String element :startdate, String element :duedate, String element :modified, DateTime element :donedate, String has_many :subtasks, String, tag: subtask element :content, String end

class TaskList def initialize(tasks) @tasks = tasks

<span class="instance-variable">@tasks_hash</span> = {}
<span class="instance-variable">@tasks</span>.each <span class="keyword">do</span> |task|
  <span class="instance-variable">@tasks_hash</span>[] = task
<span class="keyword">end</span>


def each_task(&block) @tasks.each &block end

def find(task_id) @tasks_hash[task_id] end

def root_task(task) parent = @tasks.find { |it| it.subtasks.include? } parent && root_task(parent) || task end end

class TaskProcessor def initialize(task_list, handler) @task_list = task_list @handler = handler @processed = {} end

def process @processed.clear @task_list.each_task do |task| next if @processed[] root = @task_list.root_task(task) process_task root end

<span class="instance-variable">@task_list</span>.each_task <span class="keyword">do</span> |task|
  raise <span class="string"><span class="delimiter">&quot;</span><span class="content">Task </span><span class="inline"><span class="inline-delimiter">#{</span><span class="inline-delimiter">}</span></span><span class="content"> not processed</span><span class="delimiter">&quot;</span></span> <span class="keyword">unless</span> <span class="instance-variable">@processed</span>[]
<span class="keyword">end</span>


def self.process(tasks, handler) new(tasks, handler).process end


def process_task(task, level = 0) @handler.handle(task, level) @processed[] = true process_subtasks task.subtasks, level + 1 end

def process_subtasks(subtask_ids, level) subtask_ids.each do |task_id| raise "Task #{task_id} already processed" if @processed[task_id] task = @task_list.find(task_id) process_task task, level end end end

class TaskWarriorExporter def initialize(task_list) @task_list = task_list end

def handle(task, level) status = case task.status when Dismiss deleted when Done completed when Active pending else raise "Unknown: #{task.status}" end

data = {
  <span class="key">description</span>: task.title,
  <span class="key">status</span>: status,
  <span class="key">uuid</span>: task.uuid,
<span class="keyword">if</span> task.duedate
  <span class="keyword">if</span> task.duedate == <span class="string"><span class="delimiter">'</span><span class="content">soon</span><span class="delimiter">'</span></span>
    data[<span class="symbol">:priority</span>] = <span class="string"><span class="delimiter">'</span><span class="content">H</span><span class="delimiter">'</span></span>
  <span class="keyword">else</span>
    data[<span class="symbol">:due</span>] = task.duedate
  <span class="keyword">end</span>
<span class="keyword">end</span>
data[<span class="symbol">:end</span>] = task.donedate <span class="keyword">if</span> task.donedate
data[<span class="symbol">:scheduled</span>] = task.startdate <span class="keyword">if</span> task.startdate

entry = guess_entry(task)
data[<span class="symbol">:entry</span>] = entry

subtask_uuids = <span class="keyword">do</span> |subtask_id|
  <span class="instance-variable">@task_list</span>.find(subtask_id).uuid
<span class="keyword">end</span>
<span class="keyword">if</span> subtask_uuids.any?
  data[<span class="symbol">:depends</span>] = subtask_uuids.join(<span class="string"><span class="delimiter">'</span><span class="content">,</span><span class="delimiter">'</span></span>)
<span class="keyword">end</span>
data[<span class="symbol">:tags</span>] = task.tags <span class="keyword">unless</span> task.tags.empty?
<span class="keyword">if</span> task.content
  data[<span class="symbol">:annotations</span>] = [ { <span class="key">entry</span>: entry, <span class="key">description</span>: task.content } ]
<span class="keyword">end</span>
puts data.to_json



def guess_entry(task) dates = [task.duedate, task.donedate, task.startdate].compact. reject { |it| %w(someday soon).include? it }. sort dates.first || task.modified.to_s end end

projects_file = File.expand_path ~/.local/share/gtg/projects.xml projects = HappyMapper.parse projects_file tasks_file = projects.backend.path tasks = Task.parse tasks_file task_list = tasks


Tags , , , , 1 comment no trackbacks

Current thoughts on smart contracts

Posted by matijs 31/07/2017 at 13h50

  • Writing a contract such that the law is powerless to reverse it is anti-democratic. Libertarians will probably love it, but in canceling out the ‘oppressive’ state it also cancels any protections offered by the state.
  • Trust is a fundamental basis of human interaction. Creating a trustless way of cooperating allows agents to not be held accountable for actions performed outside the contract.
  • Instead of the lame excuse ‘the law allows me to be an asshole’, we’ll get ‘the smart contract allows me to be an asshole’.

no comments no trackbacks

Private Toolbox: An Anti-Pattern

Posted by matijs 10/04/2016 at 09h21

This is an anti-pattern that has bitten me several times.

Suppose you have an object hierarchy, with a superclass Animal, and several subclasses, Worm, Snake, Dog, Centipede. The superclass defines the abstract concept move, which is realized in the subclasses in different ways, i.e., by slithering or walking. Suppose that due to other considerations, it makes no sense to derive Worm and Snake from a SlitheringAnimal, nor Dog and Centipede from a WalkingAnimal. Yet, the implementation of Worm#move and Snake#move have a lot in common, as do Dog#move and Centipede#move.

One way to solve this is to provide methods walk and slither in the superclass that can be used by the subclasses that need them. Because it makes no sense for all animals be able to walk and slither, these methods would need to be accessible only to subclasses (e.g., private in Ruby).

Thus, the superclass provides a toolbox of methods that can only be used by its subclasses to mix and match as they see fit: a Private Toolbox.

This may seem an attractive course of action, but in my experience, this becomes a terrible mess in practice.

Let’s examine what is wrong with this in more detail. I see four concrete problems:

  • It is not always clear at the point of method definition what a method’s purpose is.
  • Each subclass carries with it the baggage of extra private methods that neither it nor its subclasses actually use.
  • The superclass’ interface is effectively extended to its non-public methods,
  • New subclasses may need to share methods that are not available in the superclass.

The Animal superclass shouldn’t be responsible for the ability to slither and to move. If we need more modes, we may not always be able to add them to the superclass.

We could extract the modes of movement into separate helper classes, but in Ruby, it is more natural to create a module. Thus, there would be modules Walker and Slitherer, each included by the relevant subclasses of Animal. These modules could either define move directly, or define walk and slither. Because the methods added in the latter case would actually makes sense for the including classes, there is less need to make them private: Once could make a instance of Dog walk, either by calling move, or by calling walk directly.

This solves all four of Private Toolbox’ problems:

  • The module names reveal the purpose of the defined methods.
  • Subclasses that do not need a particular module’s methods do not include it.
  • The implementor of Animal is free to change its private methods.
  • If a new mode of transportation is needed, no changes to Animal are needed. Instead, a new module can be created that provides the relevant functionality.

Tags , , no comments no trackbacks

Minimally Intrusive SimpleCov Loading

Posted by matijs 02/04/2016 at 16h55

I always like extra developer tooling to be minimally intrusive, to avoid forcing it on others working with the same code. There are several aspects to this: Presence of extra gems in the bundle, presence and visibility of extra files in the repository, and presence of extra code in the project.

For this reason, I’ve been reluctant to introduce tools like guard or some of the Rails preloaders that came before Spring. On the other hand, no-one would be bothered by my occasional running of RuboCop, Reek or pronto.

In this light, I’ve always found SimpleCov a little too intrusive: It needs to be part of the bundle, and the normal way to set things up makes it rather prominently visible in your test or spec helper. Nothing too terrible, but I’d like to just come to a project, run something like simplecov rake spec, and have my coverage data.

I haven’t reached that blissful state of casual SimpleCov use yet, but I’m quite pleased with what we achieved for Reek.

Here’s what we did:

  • Add simplecov to the Gemfile
  • Add a .simplecov file with configuration:
SimpleCov.start do
  track_files 'lib/**/*.rb'
  # version.rb is loaded too early to test
  add_filter 'lib/reek/version.rb'

SimpleCov.at_exit do SimpleCov.result.format! SimpleCov.minimum_coverage 98.9 SimpleCov.minimum_coverage_by_file 81.4 end

  • Add -rsimplecov to the ruby_opts for our spec task:'spec') do |t|
  t.pattern = 'spec/reek/**/*_spec.rb'
  t.ruby_opts = ['-rsimplecov -Ilib -w']

This has several nice features:

First, there are no changes to spec_helper.rb. That file can get pretty cluttered, so the less has to be in there, the better.

Second, it only calculates coverage when running the full suite with rake spec. This means running just one spec file while developing won’t clobber your coverage data, and it makes running single specs a little faster since it doesn’t need to update the coverage reports.

Third, it enforces a minimum coverage per file and for the whole suite. The second point helps a lot in making this practical: Otherwise, running individual specs would almost always fail due to low coverage.

no comments no trackbacks