When someone on Mastodon asked for help with a Ruby question, my friend and business partner Markus kindly referred them to me, knowing that I’m always happy to help other Ruby developers. Their question was about executing Mastodon code from a custom script. In this post, I describe how I solved this problem using the Rails console and a custom subclass.

The problem the person wanted to solve was testing the logic Mastodon uses for verifying the ownership of a user’s websites.

For details on how Mastodon verifies websites, see the section “Link verification” in the Mastodon user profile documentation. In short, you have to add a link from your website back to your Mastodon account using either a <a rel="me" ...> link in the page body or a <link rel="me" ...> reference in its header.

Ideally, this test would use the original Mastodon codebase to make sure it worked across application updates.

Not the solution: standalone script

On my live stream, I first attempted to write a standalone script. My thought was to instantiate the VerifyLinkService class from app/services/verify_link_service.rb. I was hoping I’d get away with requiring a few dependencies from the Mastodon application, and maybe replacing a few classes with mocks. But I quickly ended up including ActionSupport and the kitchen sink, which made this approach unviable.

The solution: Rails console

In order to use the original verification code, I had to to run it in the context of the Mastodon application. The easiest way to do this is using the Rails console. However, spinning up a Rails console requires a working Rails application–database and all. Before I describe how to solve this, let’s first build the code we’re going to run inside the Rails console to test the verification.

Building the test

Maybe I would have been able to use the original VerifyLinkService class with a few tweaks, but after closer inspection, I was actually only interested in its methods perform_request! and link_back_present?. One fetches the website in question, the other checks if it contains a valid link back to Mastodon.

Unfortunately, both methods use instance variables set in the constructor using Mastodon user data. And, to make matters worse, they’re (rightfully) private methods, which meant that nobody is allowed to call them. And just when I told my viewers, “Well, nobody but the class itself, of course”, inspiration hit me: Not only a class is allowed to call its private methods, but also its child classes! All I had to do is create a subclass of VerifyLinkService, provide our test details in its constructor, and call the two methods required to execute the verification. This is what I ended up with:

class ManualVerifyLinkService < VerifyLinkService
  def initialize
    @link_back = "https://mastodon.social/@geewiz"
    @url       = "https://www.geewiz.dev"
  end

  def check
    perform_request!
    if link_back_present?
      puts "Verification successful."
    else
      puts "Verification failed."
    end
  end
end

Spinning up Mastodon

This left the problem of getting Mastodon running. The fastest way to run a Rails application with its auxiliary services and not have to do lots of manual installation is generally with Docker containers, and Mastodon is no exception. Conveniently, the Mastodon code repository already comes with a docker-compose.yml definition that contains everything we need. I found a How-To on the web that describes the launch process well. All I had to do was follow it until, and including, the step where the whole setup is started by the docker-compose up -d command.

In this state, I was able to launch a Rails console inside the already running web application container:

docker-compose exec -it web bundle exec rails console

Running the test

Into this console, I pasted the definition of my ManualVerifyLinkService class from above.

And finally, I was able to execute the verification by instantiating an object and calling its check method:

v=ManualVerifyLinkService.new
v.check

Since my website is already verified by Mastodon, the result was positive.

This was a fun experiment, and as is often the case when I help someone, I’ve learned something myself in the process.