Brakeman LSP Support

Announcing the ruby-lsp-brakeman project!

This new gem allows Brakeman scans to be integrated into code editors via ruby-lsp. Scans will run asynchronously in the background and warnings will can be shown inline in the editor.

Using Ruby-LSP-Brakeman

Add ruby-lsp-brakeman to your Gemfile:

gem 'ruby-lsp-brakeman', require: false

In VS Code

If using with VS Code, make sure to install the Ruby LSP extension.

bundle install and then restart the Ruby LSP extension to enable the add-on.

To double-check that Brakeman is running, examine the output tab in the VS Code panel for output like:

[info] (example-app) Finished initializing Ruby LSP!
[info] (example-app) [Brakeman] Activated Ruby LSP Brakeman, running initial scan
[info] (example-app) [Brakeman] Initial Brakeman scan complete - 0 warnings found

When files are saved, there should be logs like:

[info] (example-app) [Brakeman] Queued example-app/app/controllers/some_controller.rb
[info] (example-app) [Brakeman] Rescanning example-app/app/controllers/some_controller.rb
[info] (example-app) [Brakeman] Rescanned example-app/app/controllers/some_controller.rb
[info] (example-app) [Brakeman] Warnings: 0 new, 1 fixed, 2 total

Findings will show up with squiggly underlines:

Inline Brakeman warning in VS Code

Background Information

Just for those interested in what’s going on behind the scenes!

Language Server Protocol

Language Server Protocol (LSP) is a standard for communication between code editors and code-related tools. It enables tools to hook into standard events for code review, code completion, formatting, etc.

In the Ruby world, Shopify’s ruby-lsp provides a convenient implementation of LSP and the ability to build “add-ons” like ruby-lsp-brakeman.

How Brakeman is Integrated

The Brakeman add-on primarily hooks into the file change monitoring, which is triggered when a file is saved or deleted. The file is then added to a queue for rescanning. All files in the queue will be rescanned in the next scan. This is to avoid either triggering multiple concurrent scans or missing file updates because a scan was already in progress.

When the scan is complete, the warnings are reported back as a “diagnostic” to be displayed in the editor.

Interestingly, it’s also necessary to return empty sets of diagnostics (per changed file) to clear any fixed warnings that were previously reported.

Brakeman Rescanning

Unlike some other code review or formatting tools, Brakeman works across the entire application, not one file at a time. Code in one file can have effects elsewhere in the application.

A long time ago, “rescanning”/incremental scans were added to Brakeman. To achieve this, Brakeman needs to keep the entire state of the scan in memory, then attempt to update only the relevant information as files change. (This is as opposed to running scans on only a subset of files or caching results offline somehow. Side note: do not use --only-files to try to make this work!)

To know what to update internally, Brakeman would try to guess based on the file that changed (including cascading effects). This was always pretty heavy on heuristics and not very well tested. But it kind of mostly worked!

All the way back in Brakeman 5.0, the scan implementation moved away from using file names and paths to determine the type of file (e.g., controllers vs. models) to using the contents of the file instead. However, the re-scanning was not updated to use this information. Since it was still operating based on file paths, it was no longer aligned with the files being scanned by Brakeman normally.

What all this means is that rescanning has been in a broken and slowly deteriorating state since Brakeman 5.0!

With Brakeman 7.0, rescanning has been revised. For now, rescanning focuses on caching parsed files and only re-parsing changed files. The rest of the scan starts from “scratch”. Finding, reading, and parsing files is often one of the slowest parts of scans, so this should still save time for most folks.

Since caching all the parsed files introduces a bit of memory overhead, the functionality is off by default. To enable, initial scans must be run with support_rescanning: true.

Hopefully future work will be able to expand out the “incremental” part of rescanning again.

What’s Next

While the add-on generally works with VS Code, I’d love to polish it up a bit more and move to a 1.0 release.

I’m also considering if ruby-lsp-brakeman should depend on Brakeman, or if it should actually be a Brakeman dependency so everyone has it available by default. Let me know if you have thoughts on that.

Please help by testing out ruby-lsp-brakeman and sharing any feedback/bugs!

Tiny E-Ink Picture Display

After being surprised by the capabilities of a three-color e-ink display (and struggling to get it to work!), I thought I’d put together a little guide.

Hardware

The hardware I used:

In this case, I messed up a little. I already had an ESP32 feather board from Adafruit, so I should have grabbed an e-ink “feather wing” which would have plugged straight into the ESP32 board.

But since I did not do that… here’s how I wired up the display:

  • 3V3 (power) to 3V
  • GND (ground) to GND
  • SCK (clock) to SCK
  • MISO to MISO
  • MOSI to MOSI
  • ECS to 27
  • D/C to 33
  • SRCS to 15
  • SDCS to 32

The rest I didn’t connect.

Note: Don’t be tempted to use pins 12 and 13!! Pin 13 is actually shared with the onboard LED, and documentation for pin 12 says “this pin has a pull-down resistor built into it, we recommend using it as an output only”.

The names on the board don’t quite match the names in the code, so here’s a cheatsheet:

#define EPD_DC 33     // D/C
#define EPD_CS 27     // ECS
#define SRAM_CS 15    // SRCS
#define EPD_BUSY -1   // can set to -1 to not use a pin
#define EPD_RESET -1  // can set to -1 and share with chip Reset (can't deep sleep)
#define SD_CS 32      // SDCS

The libraries use defaults for the rest of the pins automatically.

Setting Up Pictures

To fit the display exactly, pictures should be 250 pixels x 122 pixels. However, the display will crop as needed.

I used Gimp and ImageMagick to make the pictures, but the main thing is the images need to be 24-bit bitmaps. I couldn’t get Gimp to save images directly to a working format.

Here are the steps I took:

  • In Gimp, crop and resize to 250x122 pixels (I prefer to crop, resize to 250 pixels wide, crop again to 122 pixels high.)
  • Set palette:
    • Go to ImageModeIndexed...
    • Select “black and white 1 bit palette”
    • OR create a new black/red/white palette and use that
    • Choose a dithering option that looks good

As far as the color to use for “red”, I believe as long as it has the r value of 255, it will work.

Gimp image mode

Then…

  • FileExport As...
  • Rename to end in .bmp and save

Then…

From the command line, run

  • convert your_image.bmp -type truecolor your_image_24.bmp

Upload

Save the pictures to the root directory of a micro SD card, then put the card in the display (for me, it’s text “down”). The slot is spring-loaded, so just push on the end to eject.

Back of e-ink display showing micro SD card inserted

Arduino

I used the Arduino IDE (2.3.2).

For the board type, use “Adafruit ESP32 Feather”. (This may seem obvious, but it took me a while to figure out which to use!)

These are the libraries I used (via the IDE’s Library Manager):

Arduino IDE

Code

Full code is available here!

Other good examples to start from:

You’ll want to adjust the pin definitions like I did above if you are following along.

ThinkInk_213_Tricolor_RW is the right type to use for the display above.

In my code, I stripped out anything not related to loading and displaying images from the SD card. If you are doing something different, try looking at the other examples.

Update these lines with the names of your images!

  int num_images = 4; // Update with number of images

  // List image paths
  char *images[num_images] = {
    "/image1.bmp",
    "/image2.bmp",
    "/image3.bmp",
    "/image4.bmp",
  };

The program will cycle through the images and update every 5 minutes (or whatever you change the delay to - recommended minimum is 3 minutes).

Results

Here are some examples. Images look best from a little distance.

E-ink display connected to ESP32; showing picture of a computer

E-ink display showing picture of a house

E-ink display showing picture of a rhinoceros

No Power?!

Yep, the main cool thing about an e-ink display is that they don’t need power to maintain the image.

However, I found just removing power from the ESP32 would cause the red pixels to “bloom” and make everything a bit pink.

To prevent this, just disconnect power from the display first. It’s possible there is a way to fix this in the code - let me know if you figure it out!

Have Fun!

E-ink display showing picture of a handsome fella

DragonRuby: Deploying on Android

This is a little less-polished-than-usual post about how to build/install Android applications with DragonRuby Pro. on a Linux system. The higher tiers of features in DragonRuby tend to be less well-documented, so here is a bit of a braindump on getting games running on a real Android device.

(Mostly to remind myself how I did all this.)

Building the Package

To be clear, you will need the “Pro” version of DragonRuby to follow this guide.

Running

dragonruby-publish --package

will generate binaries and packages for all supported platforms and dump them in builds/.

Signing

Creating a Keystore

Following the DragonRuby documentation, create a keystore file like this:

keytool -genkey -v -keystore YOUR_APP.keystore -alias your_app_name -keyalg RSA -keysize 2048 -validity 10000

This will generate a file called YOUR_APP.keystore.

keytool is probably already on your system, but if not you’ll need to install a Java JDK package using your system tools.

Getting apksigner

To install the package on your Android device, you’ll need to sign the .apk build using apksigner. I’m only going to cover one specific way of getting this program on Linux. Your experience may vary.

The easiest way to get apksigner is probably to install Android Studio and go from there. However, I prefer doing things the hard way (and not installing a whole IDE to get one binary…)

First, go to https://developer.android.com/studio/ and scroll allll the way to the bottom to “Command line tools only”. Grab the .zip file from there.

Unzip it and find the sdkmanager binary, likely in latest/bin.

Run ./sdkmanager --list | grep build-tools to find the latest version of build-tools.

Then run something like ./sdkmanager --install "build-tools;34.0.0" to install.

The files will probably end up somewhere like ../../../build-tools. In there you’ll find apksigner!

Signing the Package

apksigner sign -ks YOUR_APP.keystore builds/YOUR-GAME-android.apk

You’ll need to figure out paths for apksigner, YOUR_APP.keystore, etc.

See the official docs for more, especially if publishing to the Google Play store.

Installing on Device

Enable Debug Mode

On your Android device setup developer options, enable USB debug mode, and plug your device into your computer.

Getting adb

The adb tool can be downloaded as part of the SDK Platform tools here: https://developer.android.com/tools/releases/platform-tools#downloads.html

Install the APK

adb install builds/your-game-android.apk

Viewing Logs

To view logs from the device:

adb logcat -e your-game

where your-game is the gameid or packageid configured in mygame/metadata/game_metadata.txt.

Remote Hotload

Building, signing, and installing packages becomes a bit painful if you are doing all that during development.

How about hot-loading code on your Android device just like you can on your development machine?

Setup

When running dragonruby, it opens up a webserver on port 9001. Besides what’s obviously visible on the webpage, it’s also how your device can connect and load code dynamically.

For that all to work, your development machine and Android device need to be on the same network, and your firewall needs to allow TCP connections on port 9001.

To verify it’s working, try opening up your development machines IP on port 9001 from your Android device (e.g., visit https://YOUR.DEV.IP:9001 in a browser).

Building

Run

dragonruby-publish --package-with-remote-hotload

To create a hotloading version of the game, then sign+install like above.

Running

Make sure your are running dragonruby locally on your development machine.

Then open your game on your mobile device - it should flash “Remote hotload enabled” at the bottom if it has been built properly. This does not ensure it actually connected to the development server, though!

To test, try making a visible change to a file on your local machine and see if the change is reflected on the Android device.

Somehow, magically, the hot-loaded changes will persist even through restarts. However, only changes made while the development server is running will be picked up.

Bonus: Detecting the “Back” “Button”

I am old, so I still use the virtual “back” button on Android.

In DragonRuby, this can be detected with

args.inputs.keyboard.key_down.ac_back

For example:

if inputs.keyboard.key_down.ac_back
  gtk.request_quit
end