module ProxES class Container class ContainerError < StandardError; end # A thread safe cache class, offering only #[] and #[]= methods, # each protected by a mutex. # Ripped off from Roda - https://github.com/jeremyevans/roda class PluginCache # Create a new thread safe cache. def initialize @mutex = Mutex.new @hash = {} end # Make getting value from underlying hash thread safe. def [](key) @mutex.synchronize{@hash[key]} end # Make setting value in underlying hash thread safe. def []=(key, value) @mutex.synchronize{@hash[key] = value} end def map(&block) @mutex.synchronize{@hash.map(&block)} end end # Ripped off from Roda - https://github.com/jeremyevans/roda module Plugins # Stores registered plugins @plugins = PluginCache.new # If the registered plugin already exists, use it. Otherwise, # require it and return it. This raises a LoadError if such a # plugin doesn't exist, or a ContainerError if it exists but it does # not register itself correctly. def self.load_plugin(name) h = @plugins unless plugin = h[name] require "proxes/plugins/#{name}" raise ContainerError, "Plugin #{name} did not register itself correctly in ProxES::Container::Plugins" unless plugin = h[name] end plugin end # Register the given plugin with Container, so that it can be loaded using #plugin # with a symbol. Should be used by plugin files. Example: # # ProxES::Container::Plugins.register_plugin(:plugin_name, PluginModule) def self.register_plugin(name, mod) @plugins[name] = mod end def self.plugins @plugins end module Base module ClassMethods # Load a new plugin into the current class. A plugin can be a module # which is used directly, or a symbol represented a registered plugin # which will be required and then used. Returns nil. # # Container.plugin PluginModule # Container.plugin :csrf def plugin(plugin, *args, &block) raise ContainerError, "Cannot add a plugin to a frozen Container class" if frozen? plugin = Plugins.load_plugin(plugin) if plugin.is_a?(Symbol) plugin.load_dependencies(self, *args, &block) if plugin.respond_to?(:load_dependencies) include(plugin::InstanceMethods) if defined?(plugin::InstanceMethods) extend(plugin::ClassMethods) if defined?(plugin::ClassMethods) plugin.configure(self, *args, &block) if plugin.respond_to?(:configure) # One option is to add controllers / nav on registration: @controllers.merge(plugin.controllers) if plugin.respond_to?(:controllers) @navigation << plugin.navigation if plugin.respond_to?(:navigation) nil end def routes {} # Return a hash of controllers with their routes as keys: `{ '/users' => ProxES::Controllers::Users }` end def nav_items [] # Return an ordered list of navigation items: `[{order:0, link:'/users/' text:'Users'}, {order:1, link:'/roles/', text:'Roles'}] end def migrations Plugins.plugins.map do |_key, plugin| plugin.migration_folder if plugin.respond_to?(:migration_folder) end end end module InstanceMethods end end end extend Plugins::Base::ClassMethods plugin Plugins::Base end end