-
Notifications
You must be signed in to change notification settings - Fork 147
Plugin development
This is a collection of notes that, I hope, will eventually expand to a guide to creating Plugins for foodsoft.
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.
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.
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.
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.
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
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]
TODO
The signup plugin has an example.
TODO
The uservoice plugin is a good example.
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.
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.