Class: Diecut::PluginLoader

Inherits:
Object
  • Object
show all
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

Instance Method Summary collapse

Constructor Details

#initializePluginLoader

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_handlerObject



35
36
37
# File 'lib/diecut/plugin-loader.rb', line 35

def issue_handler
  @issue_handler ||= Diecut.issue_handler
end

#local_valiseObject

Returns the value of attribute local_valise.



24
25
26
# File 'lib/diecut/plugin-loader.rb', line 24

def local_valise
  @local_valise
end

#pluginsObject (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_sortObject



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

Returns:

  • (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

Yields:

  • (desc)


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_pathObject



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.

Returns:

  • (Boolean)


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