Simple Crystal Project Scripting Tool (nrg)

TL;DR: Source Code

Crystal has been a recent addition to my programming languages repertoire. It reminds me of my love for Ruby, but also satisfies my current concerns for performance and deployment. Crystal makes it feel like you can have your cake and eat it too. Which basically means it hits a lot of sweet spots for me. That being said it is still newish or at least the ecosystem is in its nascent stage.

While using Crystal I found myself in need of something to run a bunch of small scripts related to my project. Mostly to save on typing long commands repeatedly. I have seen a lot of people use Makefiles, and for this project I myself used a Makefile. However, I thought, wouldn't it be nice to have something like npm-scripts or like boot-clj. Unfortunately, such features aren't in the shards tool yet.

Now there is a tool I found called cake. Which is described as being like Make but for Crystal. Also, not to be confused with rake from the Ruby world. Anyways, I gave this tool a try and I had two problems with it. The first is that I found myself wishing I was just scripting with bash, and startup performance was not good. My intent was to use this for quick iterations and convenience scripts. So I felt this wouldn't fly.

After thinking about what I wanted, I came up with my own idea. This idea involves attaching small bash scripts to the shard.yml file that comes with a Crystal project. One benefit of this approach is that, it is one less file cluttering your project directory. Another advantage is that yaml is quite flexible. The command name is the key in the yaml file, with the script being the value. Thus being almost exactly like npm-scripts.

Here is an example kemal application where I have added a few convenience commands for building the project on the commands field, scripts turns out to be already taken by shards.

name: todo                                                                                                                                               
version: 0.1.0                                                                                                                                                 

authors:                                                                                                                                                       
  - fancycade <fancycade@protonmail.com>                                                                                                                       

targets:                                                                                                                                                       
  cityscape:                                                                                                                                                   
	main: src/cityscape.cr                                                                                                                                     

dependencies:                                                                                                                                                  
  kemal:                                                                                                                                                       
	github: kemalcr/kemal                                                                                                                                      
  spec-kemal:                                                                                                                                                  
	github: kemalcr/spec-kemal                                                                                                                                 
  pg:                                                                                                                                                          
	github: will/crystal-pg                                                                                                                                    

crystal: 0.32.1                                                                                                                                                

license: MIT                                                                                                                                                   

commands:                                                                                                                                                       
  run: 
	crystal run src/cityscape.cr                                                                                                                                                                                                                                                                                         
  release: 
	crystal build --release src/cityscape.cr                                                                                                                                                                                                                                                                    

Notice the run and release keys. We can use these commands by running nrg in the project directory.

nrg run

# or

nrg release

It simply looks for a shard.yml file in the project directory, looks for that key and then runs the sting value as a system command. I have also added a few quality of life features to make it more handy. The first being the ability to run multiline commands. We could do something like this by adding the pipe character after the key to denote a yaml multiline string:

commands:
  test: |
	./sql/setup.sh
	KEMAL_ENV=test crystal spec
	./sql/teardown.sh

All of this is abstracted into:

nrg test

Since nrg will run each of these lines in order.

Sometimes bash scripts require passing in a parameter. An example being the psql tool requiring a database and username when executing sql scripts. I've decided to make this possible with nrg by using ? as a special character. This is similar to passing args to a bash script. I chose ? since it has the least conflicts with bash scripting. For example my setup commands can require two parameters. I can make a command like this in my shard.yml:

commands:
  setup: ./sql/setup.sh ?1 ?2

Which is used like so:

nrg setup fancycade todo

Let's take this up a notch by refactoring our test command to use our setup and teardown commands. And this accepts the username and the database name:

commands:
  setup:
	./sql/setup.sh ?1 ?2
  teardown:
	./sql/teardown.sh ?1 ?2
  test: |
	nrg setup ?1 ?2
	KEMAL_ENV=test crystal spec
	nrg teardown ?1 ?2

Maybe you feel this is helpful, but it certainly feels like more natural way to interact with the project. The key-value nature of the yaml file provides a built in namespace that can be leveraged to build powerful abstractions. Which I find to be very Unix-esque and down right cool.

For me, this adventure is a fun thing about getting into a new language early. There is wide open space to explore and experiment with different tooling. I can imagine similar behavior going into the shards tool itself. For now I'm going to keep hacking on nrg to add whatever features I personally find useful. And that is the beauty of programming.

Content for this site is CC-BY-SA.

More Posts