Skip to content
wvengen edited this page Apr 1, 2017 · 15 revisions

This is a collection of notes that, I hope, will eventually expand to a guide to creating Plugins for foodsoft.

Creating a new plugin

In the foodsoft root directory, generate a new plugin. We keep plugins in the "lib" directory, and their names are prefixed with "foodsoft_".

rails plugin new plugins/myplugin --full --skip-gemfile

The --full option indicates that we're not only creating a rails plugin, but also an engine, which can define new views and controllers.

Now modify the generated files, at least: "foodsoft_myplugin.gemspec" and "README.md".

To enable the plugin for foodsoft, add the following line to foodsoft's "Gemfile":

gem 'foodsoft_myplugin', path: 'plugins/myplugin'

When you run the rails server, you should see an unmodified foodsoft, but with the knowledge that the new plugin code is being loaded.

Adding a configuration option

There are two kinds of configuration options: foodcoop-specific ones (as defined in "config/app_config.yml"), and Rails application configuration (which is defined in "config/application.rb", as well as in the environments). In all but a few cases, you'd probably want to use foodcoop-specific ones. For Rails configuration options, read on at the bottom.

To add a foodcoop-specific option, you don't need to do anything special - you can look at FoodsoftConfig[:my_option] to access it. Do handle the case when the option isn't present.

When adding a configuration option, please document it in the plugin's README.

enabled?

Since different foodcoops may want to use different plugins, it would be nice if plugins can be loaded without doing anything. Then when a configuration option is set, the plugin could start to do its work.

For that, we defined the enabled? method in plugins/myplugin/lib/foodsoft_myplugin.rb. A basic example would be:

require '...'

module FoodsoftMyplugin
  def self.enabled?
    FoodsoftConfig[:use_myplugin]
  end
end

Then in your hooks and deface overrides, you can add a check on FoodsoftMyplugin.enabled? to do its thing or not.

Adding to the navigation bar

Foodsoft uses simple-navigation for its navigation bar. It is possible to add items from within plugins. This is a bit of a hack, since there is currently no generally accepted way to do that within simple-navigation (but it's on their wishlist). The current approach is based on this gist.

Foodsoft's navigation is setup in config/navigation.rb. This (will) includes a call to the navigation method of all engines (plugins) when they exist. This allows a plugin to modify the menu. This is done by adding this method to the plugin's Engine class, typically in plugins/foodsoft_plugin/lib/_plugin_name/engine.rb.

Example of adding a top-level menu item:

class Engine << ::Rails::Engine
  def navigation(primary, context)
    primary.item :nice, 'Nice', nice_path
  end
end

When you want to add it at a specific position, see, for example, how the wiki plugin does it.

Example of adding a second-level item (in the existing foodcoop submenu):

class Engine << ::Rails::Engine
  def navigation(primary, context)
    return if primary[:foodcoop].nil?
    primary[:foodcoop].sub_navigation.items <<
      SimpleNavigation::Item.new(primary, :sayhi, I18n.t('sayhi.navigation.sayhi'), context.sayhi_path)
  end
end

To place the item right after some other, you can look at the messages plugin.

Please make sure the code still runs when a menu item doesn't exist - some foodcoops may choose to restructure their menu, or hide certain items alltogether.

Modifying an existing foodsoft view

Adding a new controller and view is easy - just create it in the plugin as you would do in a rails application. But adding an element to an existing page is different. This can be done in different ways.

  • Deface - the most general way, you can modify any existing view. But since it hooks deep into the templating system, it may have an unintended issue here and there.
  • Add something to the flash in a controller hook
  • Add something using content_for in a controller hook

Deface

First add the dependency to your foodsoft_sayhi.gemspec file:

s.add_dependency "deface", "~> 1.0"

and require "deface" it in your plugin's "lib/foodsoft_sayhi.rb". Now you can add overrides as explained in the deface documentation. We like to use the deface DSL.

The signup plugin is a good example using deface.

TODO expand this, incl. erb[loud]

Controller hook: add to the flash

TODO

The signup plugin has an example.

Controller hook: content_for_in_controllers

TODO

The uservoice plugin is a good example.

I18n and translations

The plugin has its own locale file in plugins/sayhi/config/locales/en.yml. To integrate with localeapp, and to aid our translators, we put these strings into foodsoft's main texts in config/locales, and manage translations there. It is still important to keep the plugin's translations into its own file so that we can know which texts belong to plugins.

Adding a Rails configuration option

To add a Rails configuration option, however, you need to define it first. (See also here).

Create a file "plugins/myplugin/lib/foodsoft_myplugin/configuration.rb" defining the plugin's configuration options:

module FoodsoftMyplugin
  class Configuration
    def initialize
      @my_option = 'The Default'
    end
    attr_accessor :my_option
  end
end

Then, in "plugins/myplugin/lib/foodsoft_myplugin.rb", include it in the engine:

require 'foodsoft_myplugin/engine'
require 'foodsoft_myplugin/configuration'

module FoodsoftMyplugin
  def self.configuration
    @configuration ||= FoodsoftMyplugin::Configuration.new
  end
end

Now you can set the option in foodsoft's "config/application.rb", or, for example, in "config/environments/production.rb". Please surround this with a test to see if the plugin is defined - so that the same configuration can be used when the plugin is disabled too:

Foodsoft::Application.configure do
  # Myplugin configuration
  if defined? FoodsoftMyplugin
    config.my_option = 'Something Else'
  end
end

When you're writing a plugin that is to be included in foodsoft, please add such a section to the bottom of both the development sample and production environment configuration.