Ruby Open Source: Zammad example

Ruby Open Source: Zammad example

Another example of an open source web app written in Ruby

ยท

4 min read

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:

Result of executing rails stats on the repo

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 with exists?

  • Checking that the only allowed default_scope is about simple ordering

  • Checking for using rand

  • Checking usages of .to_sym on strings and change to : prefix

  • Checking 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:

Code sample from MySQL DB preflight check

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::HasQueuingPriority 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

Did you find this article valuable?

Support Lucian Ghinda by becoming a sponsor. Any amount is appreciated!