Skip to content
wvengen edited this page Mar 24, 2014 · 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 lib/foodsoft_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: 'lib/foodsoft_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). The latter can be specific to the (development/production/test) environment.

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. And please document it in your plugin's "README.md".

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

Create a file "lib/foodsoft_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 "lib/foodsoft_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. And in case of a foodcoop-specific option, please add a comment to "config/app_config.yml.SAMPLE" explaining the option and a note that this plugin needs to be enabled to use it.

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 lib/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.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 lib/foodsoft_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.

Clone this wiki locally