Make your own Shard in Crystal language
Introduction
Crystal is a young and perspective language. Currently it is in the alpha stage but the language is growing very quickly, more and more people are interested in Crystal. I believe it’s a time to create a new popular shard. Shard is a project written in Crystal, like a gem for Ruby or crate for Rust. The goal of this tutorial is to show you a simple and easy way to create and publish the new shard.
Creating a Shard
To start a new project with Crystal use crystal init command:
$ crystal init app hallo
create hallo/.gitignore
create hallo/LICENSE
create hallo/README.md
create hallo/.travis.yml
create hallo/shard.yml
create hallo/src/hallo.cr
create hallo/src/hallo/version.cr
create hallo/spec/spec_helper.cr
create hallo/spec/hallo_spec.cr
Initialized empty Git repository in /home/veelenga/dev/hallo/.git/Here I’m passing app argument to crystal init command because I am making a binary program. If you want to make a library instead you have to use crystal init command with type lib. Run crystal init -h for more information.
Let’s print out again what was generated for us:
$ cd hallo
$ tree .
.
├── LICENSE
├── README.md
├── shard.yml
├── spec
│ ├── hallo_spec.cr
│ └── spec_helper.cr
└── src
├── hallo
│ └── version.cr
└── hallo.cr
3 directories, 7 filesAt the root of your project directory is placed shard.yml file. This is where you will declare your project dependencies. We will talk about it a bit later in Adding dependencies section.
spec directory is used for testing your sources. Crystal has it’s own built-in testing library called Spec and it is very similar to Ruby’s Rspec. We will write tests for our project in Writing tests section.
Code for your package is placed inside the src directory and it has the same convention as for Ruby project: one Crystal file with the same name as your shard. Other project related files are placed inside src/hallo/ directory (currently there is only version.cr).
Let’s add some code to let our shard be able to say “Hello” to the world. Here is how src/hallo.rb looks after update:
require "./hallo/*"
module Hallo
def self.say_hi
puts "Hello, world!"
end
endNow we can build our file and check whether it compiles or not:
$ crystal build src/hallo.crIf compiler successfully compiles our file, you will have a new executable file hallo in your root directory. Let’s try and run it:
$ ./halloNothing was printed. Why? Because our executable only defines a new module and a method inside and we do not call it. Let’s create a real executable and print to the console “Hello, world!”.
Adding an executable
Currently Crystal does not have a convention where to place executable files. Let’s add it to the root directory. Create ./greeter.cr file with the following content:
require "./src/*"
Hallo.say_hiLet’s build our executable and then run it again:
$ crystal build greeter.cr
$ ./greeter
Hello, world!Awesome, we can run it. But the real project can be big and it could not be so easy to build or run it. So, somewhere we need to define rules that our project requires to be build to simplify a life for other developers or project’s users. This is where Makefile comes. Here is a tiny example:
OUT_DIR=bin
all: build
build:
mkdir -p $(OUT_DIR)
crystal build --release greeter.cr -o $(OUT_DIR)/greeter
run:
$(OUT_DIR)/greeter
clean:
rm -rf $(OUT_DIR) .crystal .deps libsIn our Makefile we build our greeter.cr with --release flag that is extremely important for production applications. -o $(OUT_DIR)/greeter defines a destinations file, in our example it is bin/greeter. Let’s build it:
$ make build
mkdir -p bin
crystal build --release greeter.cr -o bin/greeterAfter running this command bin/greeter file should have been created. Run it with:
$ make run
bin/greeter
Hello, world!Congratulations, we have been just created our fancy Crystal shard and we are able to build and run it. Awesome!
Adding dependencies
Shards might not be so useful if there wouldn’t a way to easily reuse it in your project. Fortunately, Crystal is integrated with shards project to manage project dependencies. Follow the instructions to install it at the beginning. And then let’s make our project dependent on emoji.cr shard that is able to emojize strings.
Firstly, we need to add a dependency to our shard.yml file:
...
dependencies:
emoji:
github: veelenga/emoji.cr
branch: master
...Secondly, we need to load our dependencies with crystal deps command:
$ crystal deps
Updating https://github.com/veelenga/emoji.cr.git
Installing emoji (master)It will clone defined dependencies to .shards/ directory. Let’s now use emoji shard in our project. Rewrite src/hallo.cr to the following:
require "./hallo/*"
require "emoji"
module Hallo
def self.say_hi
say("Hello, world :exclamation:")
end
def self.say(message : String)
Emoji.emojize(message)
end
endOn the second line we require emoji shard and then use it to emojize a message. Let’s also rewrite a ./greeter.cr executable file:
require "./src/*"
if ARGV.empty?
# say hi if no arguments passed
puts Hallo.say_hi
else
puts Hallo.say(ARGV.first)
endHere we want to print a custom message if user passes it to the executable via command line, and print default message (“Hello, world!”) if nothing was passed. Let’s build and run it:
$ make build
$ ./bin/greeter
Hello, world ❗
$ ./bin/greeter "I :heart: Crystal"
I ❤️ CrystalWe can see that our binary works as expected and emoji shard has been successfully used as a dependency. Looks easy, right? It’s time to add some tests.
Writing tests
Writing tests for your shard is very important. It will ensure that your code works, help you to be assured that your change does not break something and help others to know that your shard does it’s job. Actually, tests are a good place to view how your project works in details.
You may say that using BDD (or TDD) practice we have to write our tests first. And you will be completely correct. But, usually, it is much easier to understand a tutorial writing code first and then tests.
Crystal has it’s own built-in testing library called Spec. Let’s add basic tests for our module:
require "./spec_helper"
describe Hallo do
describe ".say_hi" do
it "returns default message" do
Hallo.say_hi.should eq "Hello, world ❗"
end
end
describe ".say" do
it "returns emojized message if there are emojies" do
Hallo.say("Hello, smiling cat :smile_cat:")
.should eq "Hello, smiling cat 😸"
end
it "returns original message if there is no emojies" do
Hallo.say("Hello!").should eq "Hello!"
end
end
endWe can run tests with crystal spec command:
$ crystal spec
...
Finished in 1.27 milliseconds
3 examples, 0 failures, 0 errors, 0 pendingTo test your project on Travis CI, here is a sample .travis.yml file:
language: crystalIt has been already generated with crystal init command. Just add it to you project if you haven’t done it already.
Now we can write tests and test our shard on Travis. Awesome! It’s a time to document our code.
Documenting your code
Crystal documentation supports a markdown syntax. Let’s add some docs with examples:
require "./hallo/*"
require "emoji"
# The main Hallo module
module Hallo
# Says hi to the world!
#
# ```
# Hallo.say_hi #=> "Hello, world ❗"
# ```
def self.say_hi
say("Hello, world :exclamation:")
end
# Says a message to the world.
# Supports emojization.
#
# ```
# Hallo.say("I :heart: Crystal") #=> I ❤️ Crystal
# ```
def self.say(message : String)
Emoji.emojize(message)
end
endAfter that we can generate documentation locally with crystal docs command:
$ crystal docsIt will generate documentation in doc folder. Just open doc/index.html in your browser and review a pretty formatted documentation of your shard. Looks easy!
Wrap-up
This is a simple and easy to use tutorial that shows how to create the new shard in Crystal language, add an executable, dependencies, write tests and document a code. I hope you are full of ideas for new projects and you are on the way to make a new shard. Crystallians are waiting for you!
Credits
This tutorial was inspired by Make your own gem guide at RubyGems.org. Source code for this shard can be found on Github.
Available Shards
The best place to learn how to create a new shard is to look at the existed ones. The list of all available shards you may find at Crystalshards and the most popular and useful projects are at Awesome Crystal.
Leave a Comment