Class: Diecut::PluginLoader
- Inherits:
-
Object
- Object
- Diecut::PluginLoader
- Includes:
- TSort
- Defined in:
- lib/diecut/plugin-loader.rb
Defined Under Namespace
Classes: GemPlugin
Constant Summary collapse
- NO_VALUE =
Object.new.freeze
- PLUGIN_FILENAME =
:nocov:
'diecut_plugin.rb'
Instance Attribute Summary collapse
- #issue_handler ⇒ Object
-
#local_valise ⇒ Object
Returns the value of attribute local_valise.
-
#plugins ⇒ Object
readonly
Returns the value of attribute plugins.
Instance Method Summary collapse
- #add_plugin_desc(desc) ⇒ Object
- #choose_source(locations) ⇒ Object
- #component_sort ⇒ Object
- #dep_path?(from_gem, to_gem) ⇒ Boolean
- #describe_plugin(name) {|desc| ... } ⇒ Object
- #discover(prerelease) ⇒ Object
- #each_path ⇒ Object
- #from_gem(spec, path) ⇒ Object
- #from_local(path) ⇒ Object
-
#initialize ⇒ PluginLoader
constructor
A new instance of PluginLoader.
-
#latest_specs(prerelease) ⇒ Object
:nocov:.
- #load_plugins(prerelease = false) ⇒ Object
- #require_plugin(path) ⇒ Object
-
#strict_sequence?(to, from) ⇒ Boolean
Can a chain of “is after” arrows be walked from ‘from’ to ‘to’.
- #tsort_each_child(node) ⇒ Object
- #tsort_each_node(&block) ⇒ Object
Constructor Details
#initialize ⇒ PluginLoader
Returns a new instance of PluginLoader.
17 18 19 20 21 22 |
# File 'lib/diecut/plugin-loader.rb', line 17 def initialize @sources = {} @local_sources = [] @by_gem_name = Hash.new{|h,k| h[k] = []} @plugins = [] end |
Instance Attribute Details
#issue_handler ⇒ Object
35 36 37 |
# File 'lib/diecut/plugin-loader.rb', line 35 def issue_handler @issue_handler ||= Diecut.issue_handler end |
#local_valise ⇒ Object
Returns the value of attribute local_valise.
24 25 26 |
# File 'lib/diecut/plugin-loader.rb', line 24 def local_valise @local_valise end |
#plugins ⇒ Object (readonly)
Returns the value of attribute plugins.
23 24 25 |
# File 'lib/diecut/plugin-loader.rb', line 23 def plugins @plugins end |
Instance Method Details
#add_plugin_desc(desc) ⇒ Object
183 184 185 |
# File 'lib/diecut/plugin-loader.rb', line 183 def add_plugin_desc(desc) plugins << desc end |
#choose_source(locations) ⇒ Object
171 172 173 174 175 176 177 178 179 180 181 |
# File 'lib/diecut/plugin-loader.rb', line 171 def choose_source(locations) locations.each do |loc| path = loc.absolute_path if @sources.has_key?(path) return path end end fallback_location = locations.first.absolute_path issue_handler.unregistered_plugin_source(fallback_location) return fallback_location end |
#component_sort ⇒ Object
98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 |
# File 'lib/diecut/plugin-loader.rb', line 98 def component_sort unless block_given? return enum_for(:component_sort) end child_idxs = {} strongly_connected_components.each_with_index do |component, idx| component.sort_by{|node| child_idxs.fetch(node, -1) }.each do |comp| yield(comp) end component.each do |comp| tsort_each_child(comp) do |child| child_idxs[child] = idx end end end end |
#dep_path?(from_gem, to_gem) ⇒ Boolean
150 151 152 153 154 155 156 157 158 159 160 161 162 |
# File 'lib/diecut/plugin-loader.rb', line 150 def dep_path?(from_gem, to_gem) # potential to optimize this: build a map of reachablility and test # against that. closed = {} open = [from_gem] until open.empty? current = open.shift return true if current == to_gem closed[current] = true open += @by_gem_name[current.gem.name].select{|gem| !closed.has_key?(gem)} end return false end |
#describe_plugin(name) {|desc| ... } ⇒ Object
187 188 189 190 191 192 193 |
# File 'lib/diecut/plugin-loader.rb', line 187 def describe_plugin(name) source_path = choose_source(caller_locations) desc = PluginDescription.new(name, source_path) yield(desc) add_plugin_desc(desc) return desc end |
#discover(prerelease) ⇒ Object
51 52 53 54 55 56 57 58 59 60 |
# File 'lib/diecut/plugin-loader.rb', line 51 def discover(prerelease) latest_specs(prerelease).map do |spec| spec.matches_for_glob(PLUGIN_FILENAME).map do |match| from_gem(spec, match) end end local_valise.get(PLUGIN_FILENAME).present.map(&:full_path).each do |path| from_local(path) end end |
#each_path ⇒ Object
116 117 118 119 120 |
# File 'lib/diecut/plugin-loader.rb', line 116 def each_path component_sort.reverse_each do |source| yield source.path end end |
#from_gem(spec, path) ⇒ Object
62 63 64 65 66 67 68 69 |
# File 'lib/diecut/plugin-loader.rb', line 62 def from_gem(spec, path) plugin = GemPlugin.new(spec, path) @sources[path] = plugin spec.dependencies.map(&:name).each do |depname| @by_gem_name[depname] << plugin end end |
#from_local(path) ⇒ Object
71 72 73 74 75 |
# File 'lib/diecut/plugin-loader.rb', line 71 def from_local(path) source = GemPlugin.new(NO_VALUE, path) @sources[path] = source @local_sources << source end |
#latest_specs(prerelease) ⇒ Object
:nocov:
41 42 43 |
# File 'lib/diecut/plugin-loader.rb', line 41 def latest_specs(prerelease) Gem::Specification.latest_specs(prerelease) end |
#load_plugins(prerelease = false) ⇒ Object
164 165 166 167 168 169 |
# File 'lib/diecut/plugin-loader.rb', line 164 def load_plugins(prerelease = false) discover(prerelease) each_path do |path| require_plugin(path) end end |
#require_plugin(path) ⇒ Object
45 46 47 |
# File 'lib/diecut/plugin-loader.rb', line 45 def require_plugin(path) require path end |
#strict_sequence?(to, from) ⇒ Boolean
Can a chain of “is after” arrows be walked from ‘from’ to ‘to’. The rules are: A plugin defined by a gem that depends on another gem “is after” the plugin defined in the latter gem. A plugin defined in a local config file is after “more general” local files (project config is after personal is after system). All local plugins are after all gem plugins
The rationale for these rules is that decisions made later in time have more information and that decisions made closer to the problem know the problem domain better.
134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 |
# File 'lib/diecut/plugin-loader.rb', line 134 def strict_sequence?(to, from) from_source = @sources[from.source_path] to_source = @sources[to.source_path] case [from_source.gem?, to_source.gem?] when [true, true] dep_path?(to_source, from_source) when [true, false] false when [false, true] true when [false, false] @local_sources.index_of(from.source_path) <= @local_sources.index_of(to.source_path) end end |
#tsort_each_child(node) ⇒ Object
81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 |
# File 'lib/diecut/plugin-loader.rb', line 81 def tsort_each_child(node) if node.gem? @by_gem_name[node.gem.name].each do |depplugin| yield depplugin end @local_sources.each do |local| yield local end else @local_sources.drop_while do |src| src != node end.drop(1).each do |node| yield(node) end end end |
#tsort_each_node(&block) ⇒ Object
77 78 79 |
# File 'lib/diecut/plugin-loader.rb', line 77 def tsort_each_node(&block) @sources.each_value(&block) end |