Commit 682ee5be54c21ad12f78597fca0e18a6f03af5d7

Authored by Braulio Bhavamitra
1 parent 99ae59ff

plugins: 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.
lib/noosfero/plugin.rb
1 require_dependency 'noosfero' 1 require_dependency 'noosfero'
  2 +require 'noosfero/plugin/parent_methods'
2 3
3 class Noosfero::Plugin 4 class Noosfero::Plugin
4 5
@@ -14,13 +15,9 @@ class Noosfero::Plugin @@ -14,13 +15,9 @@ class Noosfero::Plugin
14 15
15 class << self 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 def should_load 22 def should_load
26 @should_load.nil? && true || @boot 23 @should_load.nil? && true || @boot
@@ -92,8 +89,14 @@ class Noosfero::Plugin @@ -92,8 +89,14 @@ class Noosfero::Plugin
92 end 89 end
93 end 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 end 100 end
98 101
99 # This is a generic method that initialize any possible filter defined by a 102 # This is a generic method that initialize any possible filter defined by a
@@ -135,7 +138,7 @@ class Noosfero::Plugin @@ -135,7 +138,7 @@ class Noosfero::Plugin
135 filters = [filters] 138 filters = [filters]
136 end 139 end
137 filters.each do |plugin_filter| 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 controller_class.send(plugin_filter[:type], filter_method, (plugin_filter[:options] || {})) 142 controller_class.send(plugin_filter[:type], filter_method, (plugin_filter[:options] || {}))
140 controller_class.send(:define_method, filter_method) do 143 controller_class.send(:define_method, filter_method) do
141 instance_exec(&plugin_filter[:block]) if environment.plugin_enabled?(plugin) 144 instance_exec(&plugin_filter[:block]) if environment.plugin_enabled?(plugin)
@@ -168,38 +171,6 @@ class Noosfero::Plugin @@ -168,38 +171,6 @@ class Noosfero::Plugin
168 @all ||= available_plugins.map{ |dir| (File.basename(dir) + "_plugin").camelize } 171 @all ||= available_plugins.map{ |dir| (File.basename(dir) + "_plugin").camelize }
169 end 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 end 174 end
204 175
205 def expanded_template(file_path, locals = {}) 176 def expanded_template(file_path, locals = {})
lib/noosfero/plugin/manager.rb
@@ -76,7 +76,7 @@ class Noosfero::Plugin::Manager @@ -76,7 +76,7 @@ class Noosfero::Plugin::Manager
76 76
77 def enabled_plugins 77 def enabled_plugins
78 @enabled_plugins ||= (Noosfero::Plugin.all & environment.enabled_plugins).map do |plugin| 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 end 80 end
81 end 81 end
82 82
lib/noosfero/plugin/parent_methods.rb 0 → 100644
@@ -0,0 +1,62 @@ @@ -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
@@ -23,7 +23,7 @@ class PluginTest &lt; ActiveSupport::TestCase @@ -23,7 +23,7 @@ class PluginTest &lt; ActiveSupport::TestCase
23 end 23 end
24 24
25 should 'returns empty hash for class method extra_blocks by default if no blocks are defined on plugin' do 25 should 'returns empty hash for class method extra_blocks by default if no blocks are defined on plugin' do
26 - 26 +
27 class SomePlugin1 < Noosfero::Plugin 27 class SomePlugin1 < Noosfero::Plugin
28 end 28 end
29 29