Ruby Open Source: Zammad example
Another example of an open source web app written in Ruby
I will probably start a series of open-source Ruby Projects. Maybe I will call it #opensource #Friday.
Zammad is an open-source ticketing system, that also offers an on-cloud product.
They have their product open-sourced on Github and it is built using (at the moment of writing this article) Ruby 3.1.3 and Rails 7
Licensing
The license is GNU AGPL
They mention on their website why they chose to make the product open source:
Each file in the repository has a license line like:
Some ideas from the open-source repo
I don't have enough time to analyse in depth the repo. So just looking around for 30 minutes, here are some things that I extracted
Stats
Running rails stats
returned the following:
Styling Guide
They use Rubocop with extra cops added. They require some cops provided by gems and custom cops written by them:
require:
- rubocop-capybara
- rubocop-factory_bot
- rubocop-faker
- rubocop-graphql
- rubocop-inflector
- rubocop-performance
- rubocop-rails
- rubocop-rspec
- ../config/initializers/inflections.rb
- ./rubocop_zammad.rb
In rubocop_zammad.rb
they are loading custom cops from .rubocop/cop/zammad
Here are some custom cops written by them:
Checking if the migration file starts with a valid timestamp
Checking usages of
find_by
to check if an Active Record exists and replace it withexists?
Checking that the only allowed
default_scope
is about simple orderingChecking for using
rand
Checking usages of
.to_sym
on strings and change to:
prefixChecking usages of
unless
and suggest using!
Checking copyright notice and adding it when missing
More about their style guide can be found at doc/developer_manual/standards
Persistence
They appear to use 3 DBs, each one having their group. One (the activerecord-nulldb-adapter) is actually a NullObject pattern implemented for Active Record.
Based on the loaded connection they do a preflight check in an initializer. This is what it looks like:
Rails.application.config.after_initialize do
Zammad::Application::Initializer::DbPreflightCheck.perform
end
and you can go check lib/zammad/application/initializer
to see what kind of checks are executed for each adapter.
This is a way to make sure that the actual DB server respects a contract they have defined in these initializers (e.g. what extensions are activated or config defaults or minimum version).
See this example of a check for MySQL from the same file:
Some gems used
argon2 - "A Ruby gem offering bindings for Argon2 password hashing"
rszr - "Rszr is an image resizer for Ruby based on the Imlib2 library. It is faster and consumes less memory than MiniMagick, GD2 and VIPS, and comes with an optional drop-in interface for Rails ActiveStorage image processing"
biz - "Time calculations using business hours"
diffy - "It provides a convenient way to generate a diff from two strings or files. Instead of reimplementing the LCS diff algorithm Diffy uses battle tested Unix diff to generate diffs, and focuses on providing a convenient interface, and getting out of your way"
chunky_png - "ChunkyPNG is a pure Ruby library to read and write PNG images and access textual metadata. It has no dependency on RMagick, or any other library for that matter"
localhost - "This gem provides a convenient API for generating per-user self-signed root certificates"
activerecord-nulldb - "NullDB is the Null Object pattern as applied to ActiveRecord database adapters. It is a database backend that translates database interactions into no-ops. Using NullDB enables you to test your model business logic - including after_save hooks - without ever touching a real database"
Design Patterns
They use service objects. There is a Service::Base
class that is empty and then there is also Service::BaseWithCurrentUser
that looks something like this:
The main method that a Service object should define is execute
but there is no enforcement of this. It is just that everything under app/services/service has this method defined.
GraphQL objects
All the logic about GraphQL is inside app/graphql, namescoped to Gql.
Jobs
They can be found at app/jobs. Job priority is defined in a concern called ApplicationJob::HasQueuingPriorit
y
that looks like this:
And all jobs have a default priority of 200 while low_priority is defined as being 300
Models
They are under app/models and there is an ApplicationModel
defined that includes some defaults like this:
There are probably a lot more interesting things to discover about the codebase but this is what I got in a short review.
Enjoyed this article?
๐ Join my Short Ruby News newsletter for weekly Ruby updates from the community and visit rubyandrails.info, a directory with learning content about Ruby.
๐ Subscribe to my Ruby and Ruby on rails courses over email at learn.shortruby.com - effortless learning anytime, anywhere
๐ค Let's connect on Ruby.social or Linkedin or Twitter where I post mainly about Ruby and Rails.
๐ฅ Follow me on my YouTube channel for short videos about Ruby