Commit 9a1c00d4886c88286d43c22d1655291d18a0abf0
Exists in
master
and in
22 other branches
Merge branch 'plugin-defined-with-module' into 'master'
Support plugin to be defined as module instead of object This makes possible for a plugin to be defined as module and have its main class defined inside it with the name Base (e.g. MyPlugin::Base). The advantages of this is to correctly scope plugins constants inside the module. There are many conflicts with the core if the plugin is defined as klass, for example: - if you define a MyPlugin::DisplayHelper you'll get the error 'warning: toplevel constant DisplayHelper referenced by MyPlugin::DisplayHelper' and your class won't be loaded unless you put a "require 'my_plugin/display_helper'" - `require` is also needed for contants with the sames of constants declared under Noosfero::Plugin. For example, if you define a MyPlugin::Manager or MyPlugin::Settings, Noosfero::Plugin::Manager or Noosfero::Plugin::Settings will be returned instead of your plugin's definition. - other hard to debug errors may also happen. This also encapsulates loading procedures into methods of Noosfero::Plugin. See merge request !482
Showing
4 changed files
with
76 additions
and
43 deletions
Show diff stats
lib/noosfero/plugin.rb
| 1 | 1 | require_dependency 'noosfero' |
| 2 | +require 'noosfero/plugin/parent_methods' | |
| 2 | 3 | |
| 3 | 4 | class Noosfero::Plugin |
| 4 | 5 | |
| ... | ... | @@ -14,13 +15,9 @@ class Noosfero::Plugin |
| 14 | 15 | |
| 15 | 16 | class << self |
| 16 | 17 | |
| 17 | - attr_writer :should_load | |
| 18 | + include Noosfero::Plugin::ParentMethods | |
| 18 | 19 | |
| 19 | - # Called for each ActiveRecord class with parents | |
| 20 | - # See http://apidock.com/rails/ActiveRecord/ModelSchema/ClassMethods/full_table_name_prefix | |
| 21 | - def table_name_prefix | |
| 22 | - @table_name_prefix ||= "#{name.to_s.underscore}_" | |
| 23 | - end | |
| 20 | + attr_writer :should_load | |
| 24 | 21 | |
| 25 | 22 | def should_load |
| 26 | 23 | @should_load.nil? && true || @boot |
| ... | ... | @@ -92,8 +89,14 @@ class Noosfero::Plugin |
| 92 | 89 | end |
| 93 | 90 | end |
| 94 | 91 | |
| 95 | - def load_plugin(plugin_name) | |
| 96 | - (plugin_name.to_s.camelize + 'Plugin').constantize | |
| 92 | + def load_plugin_identifier identifier | |
| 93 | + klass = identifier.to_s.camelize.constantize | |
| 94 | + klass = klass.const_get :Base if klass.class == Module | |
| 95 | + klass | |
| 96 | + end | |
| 97 | + | |
| 98 | + def load_plugin public_name | |
| 99 | + load_plugin_identifier "#{public_name.to_s.camelize}Plugin" | |
| 97 | 100 | end |
| 98 | 101 | |
| 99 | 102 | # This is a generic method that initialize any possible filter defined by a |
| ... | ... | @@ -135,7 +138,7 @@ class Noosfero::Plugin |
| 135 | 138 | filters = [filters] |
| 136 | 139 | end |
| 137 | 140 | filters.each do |plugin_filter| |
| 138 | - filter_method = (plugin.name.underscore.gsub('/','_') + '_' + plugin_filter[:method_name]).to_sym | |
| 141 | + filter_method = "#{plugin.identifier}_#{plugin_filter[:method_name]}".to_sym | |
| 139 | 142 | controller_class.send(plugin_filter[:type], filter_method, (plugin_filter[:options] || {})) |
| 140 | 143 | controller_class.send(:define_method, filter_method) do |
| 141 | 144 | instance_exec(&plugin_filter[:block]) if environment.plugin_enabled?(plugin) |
| ... | ... | @@ -168,38 +171,6 @@ class Noosfero::Plugin |
| 168 | 171 | @all ||= available_plugins.map{ |dir| (File.basename(dir) + "_plugin").camelize } |
| 169 | 172 | end |
| 170 | 173 | |
| 171 | - def public_name | |
| 172 | - self.name.underscore.gsub('_plugin','') | |
| 173 | - end | |
| 174 | - | |
| 175 | - def public_path file = '', relative=false | |
| 176 | - File.join "#{if relative then '' else '/' end}plugins", public_name, file | |
| 177 | - end | |
| 178 | - | |
| 179 | - def root_path | |
| 180 | - Rails.root.join('plugins', public_name) | |
| 181 | - end | |
| 182 | - | |
| 183 | - def view_path | |
| 184 | - File.join(root_path,'views') | |
| 185 | - end | |
| 186 | - | |
| 187 | - # Here the developer should specify the meta-informations that the plugin can | |
| 188 | - # inform. | |
| 189 | - def plugin_name | |
| 190 | - self.name.underscore.humanize | |
| 191 | - end | |
| 192 | - def plugin_description | |
| 193 | - _("No description informed.") | |
| 194 | - end | |
| 195 | - | |
| 196 | - def admin_url | |
| 197 | - {:controller => "#{name.underscore}_admin", :action => 'index'} | |
| 198 | - end | |
| 199 | - | |
| 200 | - def has_admin_url? | |
| 201 | - File.exists?(File.join(root_path, 'controllers', "#{name.underscore}_admin_controller.rb")) | |
| 202 | - end | |
| 203 | 174 | end |
| 204 | 175 | |
| 205 | 176 | def expanded_template(file_path, locals = {}) | ... | ... |
lib/noosfero/plugin/manager.rb
| ... | ... | @@ -76,7 +76,7 @@ class Noosfero::Plugin::Manager |
| 76 | 76 | |
| 77 | 77 | def enabled_plugins |
| 78 | 78 | @enabled_plugins ||= (Noosfero::Plugin.all & environment.enabled_plugins).map do |plugin| |
| 79 | - plugin.constantize.new(context) | |
| 79 | + Noosfero::Plugin.load_plugin_identifier(plugin).new context | |
| 80 | 80 | end |
| 81 | 81 | end |
| 82 | 82 | ... | ... |
| ... | ... | @@ -0,0 +1,62 @@ |
| 1 | +class Noosfero::Plugin | |
| 2 | + | |
| 3 | + # Plugins that are defined as modules should extend | |
| 4 | + # this module manually, for example: | |
| 5 | + # module MyPlugin | |
| 6 | + # extend Noosfero::Plugin::ParentMethods | |
| 7 | + # end | |
| 8 | + module ParentMethods | |
| 9 | + | |
| 10 | + def identifier | |
| 11 | + @identifier ||= (if self.parents.first.instance_of? Module then self.parents.first else self end).name.underscore | |
| 12 | + end | |
| 13 | + | |
| 14 | + def public_name | |
| 15 | + @public_name ||= self.identifier.gsub '_plugin', '' | |
| 16 | + end | |
| 17 | + | |
| 18 | + # Here the developer should specify the meta-informations that the plugin can | |
| 19 | + # inform. | |
| 20 | + def plugin_name | |
| 21 | + self.identifier.humanize | |
| 22 | + end | |
| 23 | + def plugin_description | |
| 24 | + _("No description informed.") | |
| 25 | + end | |
| 26 | + | |
| 27 | + # Called for each ActiveRecord model with parents | |
| 28 | + # See http://apidock.com/rails/ActiveRecord/ModelSchema/ClassMethods/full_table_name_prefix | |
| 29 | + def table_name_prefix | |
| 30 | + @table_name_prefix ||= "#{self.identifier}_" | |
| 31 | + end | |
| 32 | + | |
| 33 | + def public_path file = '', relative=false | |
| 34 | + File.join "#{if relative then '' else '/' end}plugins", public_name, file | |
| 35 | + end | |
| 36 | + | |
| 37 | + def root_path | |
| 38 | + Rails.root.join('plugins', public_name) | |
| 39 | + end | |
| 40 | + | |
| 41 | + def view_path | |
| 42 | + File.join(root_path,'views') | |
| 43 | + end | |
| 44 | + | |
| 45 | + def admin_url | |
| 46 | + {:controller => "#{self.identifier}_admin", :action => 'index'} | |
| 47 | + end | |
| 48 | + | |
| 49 | + def has_admin_url? | |
| 50 | + File.exists?(File.join(root_path, 'controllers', "#{self.identifier}_admin_controller.rb")) | |
| 51 | + end | |
| 52 | + | |
| 53 | + def controllers | |
| 54 | + @controllers ||= Dir.glob("#{self.root_path}/controllers/*/*").map do |controller_file| | |
| 55 | + next unless controller_file =~ /_controller.rb$/ | |
| 56 | + controller = File.basename(controller_file).gsub(/.rb$/, '').camelize | |
| 57 | + end.compact | |
| 58 | + end | |
| 59 | + | |
| 60 | + end | |
| 61 | + | |
| 62 | +end | ... | ... |
test/unit/plugin_test.rb