When building your app, you don’t want to reinvent the wheel. More often than not, parts of your functionality are already covered by existing Crystal packages or libraries. Commonly called shards, they’re the equivalent of gems in the Ruby world.
shards is also the name of the application that manages the dependencies of a project, like bundler in Ruby. How can you instruct your app to load one or more external shards?
Your app can list its dependencies in the file shard.yml (for Rubyists: the gemfile), which we’ll take a closer look at now. At the start of a project, it looks like this:
| name: mineral |
| version: 0.1.0 |
| |
| authors: |
| - Your-Name <your-email-address> |
| |
| targets: |
| mineral: |
| main: src/mineral.cr |
| |
| crystal: 0.22.0 |
| |
| license: MIT |
It lists the startup file of the app, together with some general info. To add external packages, we need a new dependencies section.
Let’s see how this works by adding a logging feature to our app, the katip logger developed by Güven Cenan. To keep our code short and clean, we’ll work in a project mineral_log, which copies the code from project mineral.
There are two steps to add a shard to a project.
First, let’s edit shard.yml in the root folder of mineral_log, add the following, and then save:
| dependencies: |
| katip: |
| github: guvencenanguvenal/katip |
Each external library is a dependency specified with its name and the link on GitHub to fetch its source code, so there’s no need for a central Crystal repository. Beneath the link, you can also specify a specific version to use:
| version: 0.1.2 |
Crystal won’t typically change versions on you silently, but specifying the version explicitly may help other maintainers see what you did. If, on the other hand, you need the latest changes, use this:
| branch: master |
Next, you need to install the library and add it to the current project. Go to the root folder and do: $ crystal shards, or even shorter: $ shards.
This produces the following output:
| Updating https://github.com/guvencenanguvenal/katip.git |
| Installing katip (version: 0.1.0) |
If the shard you’re adding also depended on other shards, they’ll also be installed. You can see which shards were installed with:
$ shards list
which in our case produces:
| Shards installed: |
| * katip (0.1.0) |
If you look inside the project structure, you can see what was created:
A lib folder, containing a subfolder for each of the installed dependencies. Note that the source code is installed, not the executables.
A hidden folder, .shards, containing a Git subfolder for each of the installed dependencies.
A shard.lock text file, listing all of the installed shards and their version.
Changed Dependencies | |
---|---|
The requirements of one of the dependencies might change, perhaps because you need other versions of the shards on which your app depends. Check this with $ shards check. If everything is okay, this gives the message: “Dependencies are satisfied.” If not, run the command $ shards update. |
Now that the shard is properly installed, you can start using it. First, you have to tell your app to load its code. Do this by adding the following at the start of src/mineral_log.cr:
| require "katip" |
As you saw in Combining Files with Require, this will look for its source inside the lib folder.
Including the shard is just the start of the work to integrate it. Katip also needs some configuration code before it can run, like:
| LOGGER = Katip::Logger.new |
| |
| LOGGER.configure do |config| |
| config.loglevel = Katip::LogLevel::DEBUG |
| config.logclassification = Katip::LogClassification::DATE_DAY |
| config.path = "src/katip/logfiles" |
| config.info.description = "This is the Mineral Log project." |
| config.info.project = "Mineral Log." |
| config.info.version = MineralLog::VERSION # project version |
| end |
Note how we create the logger object as a constant LOGGER, so we can access it in the module MineralLog and the class Mineral.
Now we’re all set and done, and we can start adding logging messages—For example, at startup, when creating a Mineral object, when calling to_csv, and so on:
| module MineralLog |
| LOGGER.info("app mineral_log is started!") |
| |
| min1 = Mineral.new(101, "gold", "cubic") |
| puts min1.to_csv |
| end |
| |
| class Mineral |
| getter id, name |
| property crystal_struct |
| def initialize(@id : Int32, @name : String, @crystal_struct : String) |
| LOGGER.debug("A new mineral is created!") |
| end |
| |
| def initialize(@id : Int32, logger) |
| @name = "rock" |
| @crystal_struct = "unknown" |
| LOGGER.debug("A new default mineral is created!") |
| end |
| |
| def to_s |
| puts "This is a mineral with id #{id} and is called #{name} " |
| puts "It has #{crystal_struct} as crystal structure" |
| end |
| |
| def to_csv |
| LOGGER.debug("to_csv method is called") |
| "#{id},#{name},#{crystal_struct}" |
| end |
| end |
Now you can find the logfile in src/katip/logfiles/*.json. Use the logviewer by starting up mineral_log/lib/katip/katipviewer.html in a browser:
Katip offers info, warn, debug, error, and fatal messages, so you can choose what to log and when to view it.
No Shared Dependencies | |
---|---|
In Crystal, each app has its own source copy of the shards on which it depends. These are compiled into the production executable. There are no shared dependencies, as long as the code is purely Crystal. This has several advantages:
|
18.191.168.8