Skip to content

Instantly share code, notes, and snippets.

@igor-alexandrov
Created December 2, 2019 20:21
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save igor-alexandrov/b80c1af4f786a4215a0ce331f765d796 to your computer and use it in GitHub Desktop.
Save igor-alexandrov/b80c1af4f786a4215a0ce331f765d796 to your computer and use it in GitHub Desktop.
module PluginA
DEFAULT_OPTIONS = {
c: 3,
d: 4,
}
module InstanceMethods
def plugin_a_method_a
"plugin_a_method_a"
end
end
module Y
module InstanceMethods
def plugin_a_module_y_method_a
"plugin_a_module_y_method_a"
end
end
end
end
class PluginB
module InstanceMethods
def plugin_b_method_b
"plugin_b_method_b"
end
end
end
class X
PLUGINS = [] of Nil
class PluginSettings
def all
[] of Nil
end
end
def self.plugin_settings
PluginSettings.new
end
macro inherited
{{@type}}::PLUGINS = [] of Nil
{% for plugin in @type.superclass.constant(:PLUGINS) %}
load_plugin({{plugin[:decl]}}, {{plugin[:options].double_splat}})
{% end %}
end
macro load_plugin(plugin, **args)
{% plugin = plugin.resolve %}
{% options = args %}
{% if PLUGINS.map { |e| e[:decl] }.includes?(plugin) %}
raise ArgumentError.new("Cannot load plugin {{plugin.stringify}} to {{@type}}. Plugin has already been initialized")
{% else %}
{% if plugin.constant(:DEFAULT_OPTIONS) %}
{% if !options %}
{% options = plugin.constant(:DEFAULT_OPTIONS) %}
{% end %}
{% for key in plugin.constant(:DEFAULT_OPTIONS).keys %}
{% unless options[key] %}
{{ options[key] = plugin.constant(:DEFAULT_OPTIONS)[key] }}
{% end %}
{% end %}
{% end %}
{% PLUGINS << {decl: plugin, options: options} %}
{% end %}
{% if plugin.constant(:InstanceMethods) %}
include {{plugin.constant(:InstanceMethods)}}
{% end %}
{% if plugin.constant(:ClassMethods) %}
extend {{plugin.constant(:ClassMethods)}}
{% end %}
{% if plugin.constant(:FileClassMethods) %}
class UploadedFile < Shrine::UploadedFile
extend {{plugin.constant(:FileClassMethods)}}
end
{% end %}
{% if plugin.constant(:FileMethods) %}
class UploadedFile < Shrine::UploadedFile
include {{plugin.constant(:FileMethods)}}
end
{% end %}
end
macro finalize_plugins!
class PluginsSettings
def all
{% if @type.constant(:PLUGINS) %}
{{
@type.constant(:PLUGINS).map do |plugin|
options = (plugin[:options].empty? ? nil : plugin[:options])
{
name: plugin[:decl].stringify.underscore.split("::").last,
options: options.id
}
end
}}
{% end %}
end
def [](key : Symbol | String)
all.find{ |plugin| plugin[:name] == key.to_s}.try &.[:options]
end
{% for plugin in @type.constant(:PLUGINS) %}
def {{ plugin[:decl].stringify.underscore.split("::").last.id }}
{% if plugin[:options].empty? %}
nil
{% else %}
{{ plugin[:options] }}
{% end %}
end
{% end %}
end
def self.plugin_settings
PluginsSettings.new
end
end
end
class Y < X
proc_1 = ->( x : Int32 ) { x+1 }
load_plugin(PluginA, a: 1, b: proc_1)
# load_plugin(PluginA, a: 1, b: ->( x : Int32 ) { x+1 })
finalize_plugins!
end
class Z < Y
load_plugin(PluginB)
finalize_plugins!
end
class V < Y
end
class W < X
end
puts "Y"
puts Y.plugin_settings["plugin_a"]
puts Y.plugin_settings["plugin_b"]
puts Y.plugin_settings.plugin_a
puts "Z"
puts Z.plugin_settings["plugin_b"]
puts Z.plugin_settings.plugin_b
puts "V"
puts V.plugin_settings.all
puts V.plugin_settings.plugin_a
puts "W"
puts W.plugin_settings.all
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment