Ruby Open Source: Zammad example
Tags: ruby, rails, opensource, review
Published: 2023-11-10 07:08:05

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](https://zammad.com/en) is an open-source ticketing system, that also offers an on-cloud product.


![What is Zammad](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7y73w3f1kj376vtpuhuf.png)

They have their product open-sourced on [Github](https://github.com/zammad/zammad) and it is built using (at the moment of writing this article) **Ruby 3.1.3** and **Rails 7**


![Ruby Version and Rails version](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/i0bxcirdydchkjbaa3ai.png)

## Licensing

The license is [GNU AGPL](https://github.com/zammad/zammad/blob/develop/LICENSE)


![License of the repo](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0grp9y9jlcqnhe15w2er.png)


They [mention on their website](https://zammad.com/en/company/open-source) why they chose to make the product open source:


![About open source](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rlwrltz28zzzw11huhbq.png)


Each file in the repository has a license line like:

![Copyright line in the repo](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lwhue19xnhsv4ltqq8kn.png)


## 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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pl8xrmcy248iu2vyo6h6.png)


### Styling Guide

They use Rubocop with extra cops added. They require some cops provided by gems and custom cops written by them:

```ruby
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`](https://github.com/zammad/zammad/tree/develop/.rubocop/cop/zammad)

Here are *some* custom cops written by them:

* [Checking](https://github.com/zammad/zammad/blob/develop/.rubocop/cop/zammad/correct_migration_timestamp.rb) if the migration file starts with a valid timestamp
    
* [Checking](https://github.com/zammad/zammad/blob/develop/.rubocop/cop/zammad/exists_condition.rb) usages of `find_by` to check if an Active Record exists and replace it with `exists?`
    
* [Checking](https://github.com/zammad/zammad/blob/develop/.rubocop/cop/zammad/forbid_default_scope.rb) that the only allowed `default_scope` is about simple ordering
    
* [Checking](https://github.com/zammad/zammad/blob/develop/.rubocop/cop/zammad/forbid_rand.rb) for using `rand`
    
* [Checking](https://github.com/zammad/zammad/blob/develop/.rubocop/cop/zammad/no_to_sym_on_string.rb) usages of `.to_sym` on strings and change to `:` prefix
    
* [Checking](https://github.com/zammad/zammad/blob/develop/.rubocop/cop/zammad/prefer_negated_if_over_unless.rb) usages of `unless` and suggest using `!`
    
* [Checking](https://github.com/zammad/zammad/blob/develop/.rubocop/cop/zammad/update_copyright.rb) copyright notice and adding it when missing
    

More about their style guide can be found at [`doc/developer_manual/standards`](https://github.com/zammad/zammad/blob/develop/doc/developer_manual/standards/code-style-guide.md)

### Persistence

They appear to use 3 DBs, each one having their group. One ([the activerecord-nulldb-adapter](https://github.com/nulldb/nulldb)) is actually a NullObject pattern implemented for Active Record.


![gems used for persistence](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/u0pcbne5hlpv4zvs9ddf.png)

Based on the loaded connection, they do a preflight check [in an initializer](https://github.com/zammad/zammad/blob/develop/config/initializers/db_preflight_check.rb). This is what it looks like:

```ruby
Rails.application.config.after_initialize do
  Zammad::Application::Initializer::DbPreflightCheck.perform
end
```

and you can go check [`lib/zammad/application/initializer`](https://github.com/zammad/zammad/tree/develop/lib/zammad/application/initializer/db_preflight_check) 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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7owa19zd36i7s10ssdq3.png)


### Some gems used

* [argon2](https://github.com/technion/ruby-argon2) - *"A Ruby gem offering bindings for Argon2 password hashing"*
    
* [rszr](https://github.com/mtgrosser/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](https://github.com/zendesk/biz) - *"Time calculations using business hours"*
    
* [diffy](https://github.com/samg/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](https://github.com/wvanbergen/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](https://github.com/socketry/localhost) - *"This gem provides a convenient API for generating per-user self-signed root certificates"*
    
* [activerecord-nulldb](https://github.com/nulldb/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`](https://github.com/zammad/zammad/blob/develop/app/services/service/base.rb) class that is empty and then there is also [`Service::BaseWithCurrentUser`](https://github.com/zammad/zammad/blob/develop/app/services/service/base_with_current_user.rb) that looks something like this:


![Service Base object](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/m9wxtr7s6vkulsokffyg.png)


The primary 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](https://github.com/zammad/zammad/tree/develop/app/services/service) has this method defined.

**GraphQL objects**

All the logic about GraphQL is inside [app/graphql](https://github.com/zammad/zammad/tree/develop/app/graphql), namescoped to `Gql.`

**Jobs**

They can be found at [app/jobs](https://github.com/zammad/zammad/tree/develop/app/jobs)**.** Job priority is defined in a concern called [`ApplicationJob::HasQueuingPriorit`](https://github.com/zammad/zammad/blob/develop/app/jobs/application_job/has_queuing_priority.rb)`y` that looks like this:


![Application Job](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/m2wv3w4932hxc2pbtfo2.png)



And all jobs have a default priority of **200** while low\_priority is defined as being **300**

**Models**

They are under [app/models](https://github.com/zammad/zammad/tree/develop/app/models) and there is an [`ApplicationModel`](https://github.com/zammad/zammad/blob/develop/app/models/application_model.rb) defined that includes some defaults like [this](https://github.com/zammad/zammad/blob/develop/app/models/application_model.rb):


![ApplicationModel object](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kzzyt7daywrz7tv3xc8d.png)


There are probably many 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**](https://shortruby.com/) **newsletter** for weekly Ruby updates from the community. For more Ruby **learning resources**, visit [**rubyandrails.info**](http://rubyandrails.info). You can also find me on [**Ruby.social**](https://ruby.social/@lucian) or [**Linkedin**](https://linkedin.com/in/lucianghinda) or [**Twitter**](https://x.com/lucianghinda).