Pavilion has started to package some Discourse plugins as ruby gems, starting with our subscription client.
https://rubygems.org/gems/discourse_subscription_client
Our subscription client is still a separate plugin which now loads this gem, but it’s backend is now entirely packaged as a gem and the separate plugin will soon be deprecated entirely. This is a little primer on how to do the same with your plugins that are more suited to being gems.
How to work with gems
Firstly, you need to understand how to work with ruby gems. If you’ve never created a gem before I’d recommend creating your own standard gem in isolation before you tackle working with a Discourse plugin as a gem. I’d recommend this guide
https://bundler.io/guides/creating_gem.html
How plugins inject code into Discourse
The way a Discourse plugin gem injects code into Discourse is exactly the same way a standard plugin does. The only difference is the packaging. So to work with a plugin as a gem you’ll also need to understand how a standard plugin injects code into Discourse. There are essentially two things to understand here
-
A Discourse plugin is a rails engine. You’re probably already aware of this, but you’ll need to really understand what this means. I’d recommend going through this guide on Rails engines. For example you’ll need to understand why a lot of code in a Discourse plugin’s
plugin.rb
file is wrapped in anafter_initialize
callback. -
How the Discourse initialization process works. There’s really just one file to read and understand here, namely the discourse/discourse/config/application.rb file. That is where most of the rails code is loaded, where all the plugin code is loaded and where plugins are initialized. Go through that file in some depth and understand where and how the plugin files are required and then initialized.
How a Discourse plugin as a gem works
To bring it all together you’ll then need to understand how the above two topics are synthesised in a Discourse plugin gem. In particular note the following:
-
In a Discourse plugin gem the
engine.rb
file plays a similar role to theplugin.rb
file, with a few configuration differences. Check out the subscription client gem engine.rb file and compare it with a standard plugin.rb file. -
In a Discourse plugin gem you need to mock
discourse/discourse
in your rspec tests in order to properly test the gem. You don’t need to mock the entire Discourse app, just the parts you’re testing against. You do this by creating a skeleton rails app with the specific Discourse classes and endpoints you need and loading it as an rspec support. See the subscription client gem’s mock Discourse app, and where it’s loaded in the spec rails_helper.rb.
How to load a local gem in a Discourse plugin
To load your local version of a gem in a Discourse plugin when working with that plugin and gem in development you need to do the following.
Symlink your gem folder to the relevant plugin’s gem folder. For example to work with my local version of the discourse_subscription_client gem in the discourse-subscription-client plugin I do the following
ln -s /Users/angus/discourse/gems/discourse_subscription_client /Users/angus/discourse/discourse/plugins/discourse-subscription-client/gems/3.2.1/gems
Then change symlinked gem folder in the plugin to use same name pattern as a standard gem folder, e.g.
discourse_subscription_client-0.1.0.pre11
Now when your plugin loads your Discourse plugin gem it will load your local version instead of the one on rubygems.
If you have any questions or get stuck, post here and I’ll help you out.