Modules

Decidim provides plenty of features, but sometimes you will want to customize or change the default behavior of some of them. Usually, there are two ways to do that:

  1. Creating overrides in your application

  2. Creating a new module

The first option is ideal to start hacking with your Decidim implementation but, if you want to create something that others can benefit, the best is to create a module in a gem of its own that can be installed anywhere.

There is also a third possibility and create a module that is in a sub-folder of the application itself. This approach simplifies a little the integration with your app and facilitates a future extraction to a proper external gem. You can see some examples of this in the Ajuntament of Barcelona in the folders decidim-census_sms, decidim-dataviz, decidim-ephemeral_participation, or decidim-stats. However, we do not recommend this unless you are more in a testing phase and not sure if your work is going to be released more generically (most of the times this is the first approach for isolating the features). See more details about the sub-folder approach at Code customization.

By creating a new module, you can override almost anything or provide new functionalities. The best way to proceed is to first decide what you need, then take a look at how similar things have been done previously in:

  • Other external modules

  • Other Decidim instances, and from there extract the feature to a module

  • Decidim source code itself: take into account that Decidim is a bunch of separated modules, so there is much to be learned by looking at the code).

  • Other Rails applications or documentation, because yes, this is Ruby on Rails in the end.

Regarding the type of features you want to develop, these are some typical cases:

  • Create a new component or a new participatory space: For this task, Decidim is well prepared already, and when creating a new module by default (running decidim --component my-new-module), it creates a scaffolding for it.

  • Add new routes without many relations to existing features. Then just follow standard guides for creating engines for Ruby on Rails.

  • Creating a verification handler: very typical scenario. Some verification methods are worth extracting to a module as can be very standard.

  • Add content blocks. Content blocks are used currently in the homepage and processes groups, they can transform the user experience quite a lot!

  • Override existing view: This is quite common but more delicate, it consists of creating the same app/view/some-decidim-view-file file in your module to replace the original one. It is easy but you need to define a strategy for updating your overridden file every time the original in Decidim source code is changed. Some people use the gem Deface for that too.

  • Override other classes or modules in Decidim: Similar as before, but instead of just overriding files you usually take advantage of the technique of "monkey patching", something that the Ruby language is especially well suited for. Again, you need to be careful between Decidim upgrades if the original files change.

Note also that the two last ones might have conflicts with other modules, so it is worth having a nice suite of tests, both in modules and specific Decidim implementations.

There is a list of modules that it is updated from time to time, but if you are looking for technology implementations, take a look at these (please contribute to this list if you are implementing something new!):

In the case of dealing with more advanced overrides, we recommend implementing some tests that take into account the original files from which the override was created and run it every time a Decidim version is upgraded. Take this checksum checker as an example.

Recommendations

First and foremost, do not be afraid to try and start hacking. Once you feel confident enough, read the recommendations and best practices below. Remember also that this is free software, so you can do whatever you want in the end. These are some opinions on how to improve the quality of the software, but they are not hard rules.

  • To be programmed in English (variables, method and class names, comments, etc)

  • To have tests and continuous integration with good test coverage

  • To have documentation in English, explaining:

    1. all the available commands (rake tasks and such)

    2. screenshots of the admin and participant UI

    3. steps to install it

    4. feel free to add in the README if you want who’s developing/sponsoring it:

      • The gem has been developed by $Your_Employer

      • Development of this gem has been sponsored by $Your_Customer

    5. steps to run the tests locally

    6. how do you want to accept contributions

  • To follow our same rules regarding code styling

  • To have a license file that is compatible with Decidim license (GPL Affero 3)

  • To have a valid .gemspec file

  • To follow the Decidim Social Contract

  • To have a description and other metadata (ie tags) on GitHub or another platform, so it is more discoverable

  • Has good i18 support (all the strings that could be translated are in config/locales/en.yml)

  • If you upload it to GitHub, do it with the naming decidim-module-<engine_name>, so it is easier to find on the dependency graph. See discussion at GitHub.

  • To use Decidim features and APIs when relevant:

    1. Using the Admin panel

    2. Generate logs on Admin panel if admins can operate on it

    3. GraphQL API

    4. Download your data

    5. Endorsable

    6. Followable

    7. Embeddable

    8. Notifications

    9. If it is a new space, then it should be compatible with the "Context help"

  • Upload the Gem to Rubygems.org so it is easier to deploy to other apps

  • To contact us so we can publish it at Modules page

Types

You can have multiple modules types:

Example

A typical engine looks like the following:

module Decidim
  module Verifications
    module MyVerifier
      # This is an engine that authorizes users by doing a custom verification.
      class Engine < ::Rails::Engine
        isolate_namespace Decidim::Verifications::MyVerifier

        paths["db/migrate"] = nil
        paths["lib/tasks"] = nil

        routes do
          resource :authorizations, only: [:new, :create, :edit, :update], as: :authorization

          root to: "authorizations#new"
        end

        # This is a Decidim::Verifications specific initializer
        initializer "decidim_verifications_my_verifier.verification_workflow" do |_app|
          Decidim::Verifications.register_workflow(:my_verifier) do |workflow|
            workflow.engine = Decidim::Verifications::MyVerifier::Engine
          end
        end

        # more initializers here...

      end
    end
  end
end

It is a standard Ruby on Rails engine.

Decidim gotchas with engines

If you have an external module that defines rake tasks and more than one engine, you probably want to add paths["lib/tasks"]= nil to all engines but the main one, otherwise the tasks you define are probably running multiple times unintentionally. Check #3892 for more details.