bundler 1.12.6 → 1.13.0.pre.1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of bundler might be problematic. Click here for more details.

Files changed (110) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop_todo.yml +15 -13
  3. data/.travis.yml +2 -2
  4. data/CHANGELOG.md +40 -3
  5. data/CONTRIBUTING.md +9 -5
  6. data/DEVELOPMENT.md +30 -18
  7. data/ISSUES.md +26 -22
  8. data/Rakefile +15 -4
  9. data/bin/rubocop +1 -1
  10. data/bundler.gemspec +2 -2
  11. data/exe/bundle +7 -0
  12. data/lib/bundler.rb +6 -3
  13. data/lib/bundler/capistrano.rb +1 -1
  14. data/lib/bundler/cli.rb +27 -10
  15. data/lib/bundler/cli/binstubs.rb +2 -0
  16. data/lib/bundler/cli/exec.rb +1 -1
  17. data/lib/bundler/cli/install.rb +87 -56
  18. data/lib/bundler/cli/lock.rb +5 -0
  19. data/lib/bundler/cli/open.rb +3 -1
  20. data/lib/bundler/cli/outdated.rb +8 -8
  21. data/lib/bundler/cli/plugin.rb +23 -0
  22. data/lib/bundler/cli/update.rb +2 -2
  23. data/lib/bundler/cli/viz.rb +3 -0
  24. data/lib/bundler/definition.rb +72 -16
  25. data/lib/bundler/dsl.rb +19 -7
  26. data/lib/bundler/endpoint_specification.rb +2 -2
  27. data/lib/bundler/env.rb +2 -2
  28. data/lib/bundler/errors.rb +15 -1
  29. data/lib/bundler/fetcher.rb +5 -2
  30. data/lib/bundler/fetcher/compact_index.rb +2 -2
  31. data/lib/bundler/fetcher/dependency.rb +8 -4
  32. data/lib/bundler/fetcher/downloader.rb +1 -1
  33. data/lib/bundler/friendly_errors.rb +1 -1
  34. data/lib/bundler/index.rb +29 -36
  35. data/lib/bundler/inline.rb +14 -4
  36. data/lib/bundler/installer.rb +22 -3
  37. data/lib/bundler/installer/gem_installer.rb +1 -1
  38. data/lib/bundler/installer/standalone.rb +1 -1
  39. data/lib/bundler/mirror.rb +4 -4
  40. data/lib/bundler/plugin.rb +156 -0
  41. data/lib/bundler/plugin/api.rb +56 -0
  42. data/lib/bundler/plugin/dsl.rb +29 -0
  43. data/lib/bundler/plugin/index.rb +88 -0
  44. data/lib/bundler/plugin/installer.rb +99 -0
  45. data/lib/bundler/plugin/installer/git.rb +38 -0
  46. data/lib/bundler/plugin/installer/rubygems.rb +27 -0
  47. data/lib/bundler/plugin/source_list.rb +24 -0
  48. data/lib/bundler/postit_trampoline.rb +54 -0
  49. data/lib/bundler/psyched_yaml.rb +1 -1
  50. data/lib/bundler/remote_specification.rb +5 -5
  51. data/lib/bundler/resolver.rb +27 -29
  52. data/lib/bundler/ruby_version.rb +29 -3
  53. data/lib/bundler/rubygems_ext.rb +3 -1
  54. data/lib/bundler/rubygems_integration.rb +10 -4
  55. data/lib/bundler/runtime.rb +1 -16
  56. data/lib/bundler/settings.rb +19 -15
  57. data/lib/bundler/setup.rb +1 -0
  58. data/lib/bundler/shared_helpers.rb +3 -0
  59. data/lib/bundler/source.rb +4 -3
  60. data/lib/bundler/source/gemspec.rb +13 -0
  61. data/lib/bundler/source/git.rb +4 -3
  62. data/lib/bundler/source/git/git_proxy.rb +9 -5
  63. data/lib/bundler/source/path.rb +11 -2
  64. data/lib/bundler/source/rubygems.rb +28 -15
  65. data/lib/bundler/source_list.rb +5 -1
  66. data/lib/bundler/spec_set.rb +3 -3
  67. data/lib/bundler/ssl_certs/index.rubygems.org/GlobalSignRootCA.pem +21 -0
  68. data/lib/bundler/ssl_certs/rubygems.org/{AddTrustExternalCARoot-2048.pem → AddTrustExternalCARoot.pem} +0 -0
  69. data/lib/bundler/templates/newgem/CODE_OF_CONDUCT.md.tt +54 -29
  70. data/lib/bundler/templates/newgem/newgem.gemspec.tt +5 -2
  71. data/lib/bundler/templates/newgem/spec/newgem_spec.rb.tt +3 -3
  72. data/lib/bundler/templates/newgem/spec/spec_helper.rb.tt +2 -2
  73. data/lib/bundler/ui/shell.rb +4 -0
  74. data/lib/bundler/ui/silent.rb +3 -0
  75. data/lib/bundler/uri_credentials_filter.rb +36 -0
  76. data/lib/bundler/vendor/compact_index_client/lib/compact_index_client/updater.rb +1 -1
  77. data/lib/bundler/vendor/molinillo/lib/molinillo/delegates/resolution_state.rb +50 -0
  78. data/lib/bundler/vendor/molinillo/lib/molinillo/delegates/specification_provider.rb +80 -0
  79. data/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph.rb +56 -144
  80. data/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/action.rb +35 -0
  81. data/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/add_edge_no_circular.rb +58 -0
  82. data/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/add_vertex.rb +61 -0
  83. data/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/detach_vertex_named.rb +53 -0
  84. data/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/log.rb +114 -0
  85. data/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/set_payload.rb +45 -0
  86. data/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/tag.rb +35 -0
  87. data/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/vertex.rb +123 -0
  88. data/lib/bundler/vendor/molinillo/lib/molinillo/gem_metadata.rb +1 -1
  89. data/lib/bundler/vendor/molinillo/lib/molinillo/resolution.rb +46 -51
  90. data/lib/bundler/vendor/molinillo/lib/molinillo/state.rb +4 -2
  91. data/lib/bundler/vendor/postit/lib/postit.rb +15 -0
  92. data/lib/bundler/vendor/postit/lib/postit/environment.rb +44 -0
  93. data/lib/bundler/vendor/postit/lib/postit/installer.rb +28 -0
  94. data/lib/bundler/vendor/postit/lib/postit/parser.rb +21 -0
  95. data/lib/bundler/vendor/postit/lib/postit/setup.rb +12 -0
  96. data/lib/bundler/vendor/postit/lib/postit/version.rb +3 -0
  97. data/lib/bundler/version.rb +1 -1
  98. data/lib/bundler/vlad.rb +1 -1
  99. data/lib/bundler/yaml_serializer.rb +67 -0
  100. data/man/bundle-install.ronn +10 -5
  101. data/man/bundle-package.ronn +7 -6
  102. data/man/bundle-platform.ronn +1 -1
  103. data/man/bundle-update.ronn +5 -2
  104. data/man/bundle.ronn +5 -5
  105. data/man/gemfile.5.ronn +32 -28
  106. metadata +37 -12
  107. data/lib/bundler/ssl_certs/Fastly.pem +0 -82
  108. data/lib/bundler/ssl_certs/GlobalSignOrganizationValidationCA.pem +0 -26
  109. data/lib/bundler/ssl_certs/GlobalSignRoot.pem +0 -18
  110. data/lib/bundler/ssl_certs/index.rubygems.org/GlobalSignRoot.pem +0 -18
@@ -58,7 +58,7 @@ module Bundler
58
58
  raise NetworkDownError, "Could not reach host #{uri.host}. Check your network " \
59
59
  "connection and try again."
60
60
  else
61
- raise HTTPError, "Network error while fetching #{uri}"
61
+ raise HTTPError, "Network error while fetching #{URICredentialsFilter.credential_filtered_uri(uri)}"
62
62
  end
63
63
  end
64
64
  end
@@ -63,7 +63,7 @@ module Bundler
63
63
 
64
64
  - What happened instead?
65
65
 
66
- Instead, what actually happened was...
66
+ Instead, what happened was...
67
67
 
68
68
 
69
69
  Error details
@@ -21,15 +21,14 @@ module Bundler
21
21
  @sources = []
22
22
  @cache = {}
23
23
  @specs = Hash.new {|h, k| h[k] = {} }
24
- @all_specs = Hash.new {|h, k| h[k] = [] }
24
+ @all_specs = Hash.new {|h, k| h[k] = EMPTY_SEARCH }
25
25
  end
26
26
 
27
27
  def initialize_copy(o)
28
- super
29
- @sources = @sources.dup
28
+ @sources = o.sources.dup
30
29
  @cache = {}
31
30
  @specs = Hash.new {|h, k| h[k] = {} }
32
- @all_specs = Hash.new {|h, k| h[k] = [] }
31
+ @all_specs = Hash.new {|h, k| h[k] = EMPTY_SEARCH }
33
32
 
34
33
  o.specs.each do |name, hash|
35
34
  @specs[name] = hash.dup
@@ -49,7 +48,7 @@ module Bundler
49
48
  end
50
49
 
51
50
  def search_all(name)
52
- all_matches = @all_specs[name] + local_search(name)
51
+ all_matches = local_search(name) + @all_specs[name]
53
52
  @sources.each do |source|
54
53
  all_matches.concat(source.search_all(name))
55
54
  end
@@ -60,19 +59,18 @@ module Bundler
60
59
  # about, returning all of the results.
61
60
  def search(query, base = nil)
62
61
  results = local_search(query, base)
63
- seen = Set.new(results.map {|spec| [spec.name, spec.version, spec.platform] })
62
+ seen = results.map(&:full_name).to_set
64
63
 
65
64
  @sources.each do |source|
66
65
  source.search(query, base).each do |spec|
67
- lookup = [spec.name, spec.version, spec.platform]
68
- unless seen.include?(lookup)
69
- results << spec
70
- seen << lookup
71
- end
66
+ results << spec if seen.add?(spec.full_name)
72
67
  end
73
68
  end
74
69
 
75
- results.sort_by {|s| [s.version, s.platform.to_s == RUBY ? NULL : s.platform.to_s] }
70
+ results.sort_by do |s|
71
+ platform_string = s.platform.to_s
72
+ [s.version, platform_string == RUBY ? NULL : platform_string]
73
+ end
76
74
  end
77
75
 
78
76
  def local_search(query, base = nil)
@@ -90,14 +88,15 @@ module Bundler
90
88
 
91
89
  def <<(spec)
92
90
  @specs[spec.name][spec.full_name] = spec
93
-
94
91
  spec
95
92
  end
96
93
 
97
94
  def each(&blk)
95
+ return enum_for(:each) unless blk
98
96
  specs.values.each do |spec_sets|
99
97
  spec_sets.values.each(&blk)
100
98
  end
99
+ sources.each {|s| s.each(&blk) }
101
100
  end
102
101
 
103
102
  # returns a list of the dependencies
@@ -109,17 +108,17 @@ module Bundler
109
108
 
110
109
  def dependency_names
111
110
  names = []
112
- each {|s| names.push(*s.dependencies.map(&:name)) }
111
+ each {|s| names.concat(s.dependencies.map(&:name)) }
113
112
  names.uniq
114
113
  end
115
114
 
116
115
  def use(other, override_dupes = false)
117
116
  return unless other
118
117
  other.each do |s|
119
- if (dupes = search_by_spec(s)) && dupes.any?
120
- @all_specs[s.name] = [s] + dupes
118
+ if (dupes = search_by_spec(s)) && !dupes.empty?
119
+ # safe to << since it's a new array when it has contents
120
+ @all_specs[s.name] = dupes << s
121
121
  next unless override_dupes
122
- self << s
123
122
  end
124
123
  self << s
125
124
  end
@@ -135,10 +134,16 @@ module Bundler
135
134
  def ==(other)
136
135
  all? do |spec|
137
136
  other_spec = other[spec].first
138
- other_spec && (spec.dependencies & other_spec.dependencies).empty? && spec.source == other_spec.source
137
+ other_spec && dependencies_eql?(spec, other_spec) && spec.source == other_spec.source
139
138
  end
140
139
  end
141
140
 
141
+ def dependencies_eql?(spec, other_spec)
142
+ deps = spec.dependencies.select {|d| d.type != :development }
143
+ other_deps = other_spec.dependencies.select {|d| d.type != :development }
144
+ Set.new(deps) == Set.new(other_deps)
145
+ end
146
+
142
147
  def add_source(index)
143
148
  raise ArgumentError, "Source must be an index, not #{index.class}" unless index.is_a?(Index)
144
149
  @sources << index
@@ -154,8 +159,10 @@ module Bundler
154
159
  def search_by_dependency(dependency, base = nil)
155
160
  @cache[base || false] ||= {}
156
161
  @cache[base || false][dependency] ||= begin
157
- specs = specs_by_name(dependency.name) + (base || [])
162
+ specs = specs_by_name(dependency.name)
163
+ specs += base if base
158
164
  found = specs.select do |spec|
165
+ next true if spec.source.is_a?(Source::Gemspec)
159
166
  if base # allow all platforms when searching from a lockfile
160
167
  dependency.matches_spec?(spec)
161
168
  else
@@ -174,25 +181,11 @@ module Bundler
174
181
  end
175
182
  end
176
183
 
184
+ EMPTY_SEARCH = [].freeze
185
+
177
186
  def search_by_spec(spec)
178
187
  spec = @specs[spec.name][spec.full_name]
179
- spec ? [spec] : []
180
- end
181
-
182
- if RUBY_VERSION < "1.9"
183
- def same_version?(a, b)
184
- regex = /^(.*?)(?:\.0)*$/
185
- a.to_s[regex, 1] == b.to_s[regex, 1]
186
- end
187
- else
188
- def same_version?(a, b)
189
- a == b
190
- end
191
- end
192
-
193
- def spec_satisfies_dependency?(spec, dep)
194
- return false unless dep.name == spec.name
195
- dep.requirement.satisfied_by?(spec.version)
188
+ spec ? [spec] : EMPTY_SEARCH
196
189
  end
197
190
  end
198
191
  end
@@ -41,6 +41,7 @@ def gemfile(install = false, options = {}, &gemfile)
41
41
  end
42
42
  ENV["BUNDLE_GEMFILE"] ||= "Gemfile"
43
43
 
44
+ Bundler::Plugin.gemfile_install(&gemfile) if Bundler.settings[:plugins]
44
45
  builder = Bundler::Dsl.new
45
46
  builder.instance_eval(&gemfile)
46
47
 
@@ -48,8 +49,17 @@ def gemfile(install = false, options = {}, &gemfile)
48
49
  def definition.lock(*); end
49
50
  definition.validate_ruby!
50
51
 
51
- if install
52
- Bundler.ui = ui
52
+ missing_specs = proc do
53
+ begin
54
+ !definition.missing_specs.empty?
55
+ rescue Bundler::GemNotFound, Bundler::GitError
56
+ definition.instance_variable_set(:@index, nil)
57
+ true
58
+ end
59
+ end
60
+
61
+ Bundler.ui = ui if install
62
+ if install || missing_specs.call
53
63
  Bundler::Installer.install(Bundler.root, definition, :system => true)
54
64
  Bundler::Installer.post_install_messages.each do |name, message|
55
65
  Bundler.ui.info "Post-install message from #{name}:\n#{message}"
@@ -58,7 +68,7 @@ def gemfile(install = false, options = {}, &gemfile)
58
68
 
59
69
  runtime = Bundler::Runtime.new(nil, definition)
60
70
  runtime.setup.require
61
-
71
+ ensure
62
72
  bundler_module = class << Bundler; self; end
63
- bundler_module.send(:define_method, :root, old_root)
73
+ bundler_module.send(:define_method, :root, old_root) if old_root
64
74
  end
@@ -68,6 +68,7 @@ module Bundler
68
68
  end
69
69
 
70
70
  resolve_if_need(options)
71
+ ensure_specs_are_compatible!
71
72
  install(options)
72
73
 
73
74
  lock unless Bundler.settings[:frozen]
@@ -146,9 +147,9 @@ module Bundler
146
147
  private
147
148
 
148
149
  # the order that the resolver provides is significant, since
149
- # dependencies might actually affect the installation of a gem.
150
+ # dependencies might affect the installation of a gem.
150
151
  # that said, it's a rare situation (other than rake), and parallel
151
- # installation is just SO MUCH FASTER. so we let people opt in.
152
+ # installation is SO MUCH FASTER. so we let people opt in.
152
153
  def install(options)
153
154
  force = options["force"]
154
155
  jobs = 1
@@ -156,6 +157,24 @@ module Bundler
156
157
  install_in_parallel jobs, options[:standalone], force
157
158
  end
158
159
 
160
+ def ensure_specs_are_compatible!
161
+ system_ruby = Bundler::RubyVersion.system
162
+ rubygems_version = Gem::Version.create(Gem::VERSION)
163
+ specs.each do |spec|
164
+ if required_ruby_version = spec.required_ruby_version
165
+ unless required_ruby_version.satisfied_by?(system_ruby.gem_version)
166
+ raise InstallError, "#{spec.full_name} requires ruby version #{required_ruby_version}, " \
167
+ "which is incompatible with the current version, #{system_ruby}"
168
+ end
169
+ end
170
+ next unless required_rubygems_version = spec.required_rubygems_version
171
+ unless required_rubygems_version.satisfied_by?(rubygems_version)
172
+ raise InstallError, "#{spec.full_name} requires rubygems version #{required_rubygems_version}, " \
173
+ "which is incompatible with the current version, #{rubygems_version}"
174
+ end
175
+ end
176
+ end
177
+
159
178
  def can_install_in_parallel?
160
179
  if Bundler.rubygems.provides?(">= 2.1.0")
161
180
  true
@@ -185,7 +204,7 @@ module Bundler
185
204
  local = Bundler.ui.silence do
186
205
  begin
187
206
  tmpdef = Definition.build(Bundler.default_gemfile, Bundler.default_lockfile, nil)
188
- true unless tmpdef.new_platform? || tmpdef.missing_specs.any?
207
+ true unless tmpdef.new_platform? || tmpdef.missing_dependencies.any?
189
208
  rescue BundlerError
190
209
  end
191
210
  end
@@ -39,7 +39,7 @@ module Bundler
39
39
  end
40
40
 
41
41
  def handle_exception(e)
42
- # if install hook failed or gem signature is bad, just die
42
+ # Die if install hook failed or gem signature is bad.
43
43
  raise e if e.is_a?(Bundler::InstallHookError) || e.is_a?(Bundler::SecurityError)
44
44
  # other failure, likely a native extension build failure
45
45
  Bundler.ui.info ""
@@ -38,7 +38,7 @@ module Bundler
38
38
  end
39
39
 
40
40
  def bundler_path
41
- File.join(Bundler.settings[:path], "bundler")
41
+ Bundler.root.join(Bundler.settings[:path], "bundler")
42
42
  end
43
43
 
44
44
  def gem_path(path, spec)
@@ -32,10 +32,10 @@ module Bundler
32
32
 
33
33
  def parse(key, value)
34
34
  config = MirrorConfig.new(key, value)
35
- if config.all?
36
- mirror = @all
35
+ mirror = if config.all?
36
+ @all
37
37
  else
38
- mirror = (@mirrors[config.uri] = @mirrors[config.uri] || Mirror.new)
38
+ (@mirrors[config.uri] = @mirrors[config.uri] || Mirror.new)
39
39
  end
40
40
  config.update_mirror(mirror)
41
41
  end
@@ -177,7 +177,7 @@ module Bundler
177
177
  # a given mirror.
178
178
  #
179
179
  # One mirror may correspond to many different addresses, both
180
- # because of it having many dns entries or just because
180
+ # because of it having many dns entries or because
181
181
  # the network interface is both ipv4 and ipv5
182
182
  class MirrorSockets
183
183
  def initialize(mirror)
@@ -0,0 +1,156 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bundler
4
+ module Plugin
5
+ autoload :API, "bundler/plugin/api"
6
+ autoload :DSL, "bundler/plugin/dsl"
7
+ autoload :Index, "bundler/plugin/index"
8
+ autoload :Installer, "bundler/plugin/installer"
9
+ autoload :SourceList, "bundler/plugin/source_list"
10
+
11
+ class MalformattedPlugin < PluginError; end
12
+ class UndefinedCommandError < PluginError; end
13
+
14
+ PLUGIN_FILE_NAME = "plugins.rb".freeze
15
+
16
+ module_function
17
+
18
+ @commands = {}
19
+
20
+ # Installs a new plugin by the given name
21
+ #
22
+ # @param [Array<String>] names the name of plugin to be installed
23
+ # @param [Hash] options various parameters as described in description
24
+ # @option options [String] :source rubygems source to fetch the plugin gem from
25
+ # @option options [String] :version (optional) the version of the plugin to install
26
+ def install(names, options)
27
+ paths = Installer.new.install(names, options)
28
+
29
+ save_plugins paths
30
+ rescue PluginError => e
31
+ paths.values.map {|path| Bundler.rm_rf(path) } if paths
32
+ Bundler.ui.error "Failed to install plugin #{name}: #{e.message}\n #{e.backtrace.join("\n ")}"
33
+ end
34
+
35
+ # Evaluates the Gemfile with a limited DSL and installs the plugins
36
+ # specified by plugin method
37
+ #
38
+ # @param [Pathname] gemfile path
39
+ def gemfile_install(gemfile = nil, &inline)
40
+ if block_given?
41
+ builder = DSL.new
42
+ builder.instance_eval(&inline)
43
+ definition = builder.to_definition(nil, true)
44
+ else
45
+ definition = DSL.evaluate(gemfile, nil, {})
46
+ end
47
+ return unless definition.dependencies.any?
48
+
49
+ plugins = Installer.new.install_definition(definition)
50
+
51
+ save_plugins plugins
52
+ end
53
+
54
+ # The index object used to store the details about the plugin
55
+ def index
56
+ @index ||= Index.new
57
+ end
58
+
59
+ # The directory root to all plugin related data
60
+ def root
61
+ @root ||= Bundler.user_bundle_path.join("plugin")
62
+ end
63
+
64
+ # The cache directory for plugin stuffs
65
+ def cache
66
+ @cache ||= root.join("cache")
67
+ end
68
+
69
+ # To be called via the API to register to handle a command
70
+ def add_command(command, cls)
71
+ @commands[command] = cls
72
+ end
73
+
74
+ # Checks if any plugins handles the command
75
+ def command?(command)
76
+ !index.command_plugin(command).nil?
77
+ end
78
+
79
+ # To be called from Cli class to pass the command and argument to
80
+ # approriate plugin class
81
+ def exec_command(command, args)
82
+ raise UndefinedCommandError, "Command #{command} not found" unless command? command
83
+
84
+ load_plugin index.command_plugin(command) unless @commands.key? command
85
+
86
+ @commands[command].new.exec(command, args)
87
+ end
88
+
89
+ # currently only intended for specs
90
+ #
91
+ # @return [String, nil] installed path
92
+ def installed?(plugin)
93
+ Index.new.installed?(plugin)
94
+ end
95
+
96
+ # Post installation processing and registering with index
97
+ #
98
+ # @param [Hash] plugins mapped to their installtion path
99
+ def save_plugins(plugins)
100
+ plugins.each do |name, path|
101
+ path = Pathname.new path
102
+ validate_plugin! path
103
+ register_plugin name, path
104
+ Bundler.ui.info "Installed plugin #{name}"
105
+ end
106
+ end
107
+
108
+ # Checks if the gem is good to be a plugin
109
+ #
110
+ # At present it only checks whether it contains plugins.rb file
111
+ #
112
+ # @param [Pathname] plugin_path the path plugin is installed at
113
+ # @raise [Error] if plugins.rb file is not found
114
+ def validate_plugin!(plugin_path)
115
+ plugin_file = plugin_path.join(PLUGIN_FILE_NAME)
116
+ raise MalformattedPlugin, "#{PLUGIN_FILE_NAME} was not found in the plugin!" unless plugin_file.file?
117
+ end
118
+
119
+ # Runs the plugins.rb file in an isolated namespace, records the plugin
120
+ # actions it registers for and then passes the data to index to be stored.
121
+ #
122
+ # @param [String] name the name of the plugin
123
+ # @param [Pathname] path the path where the plugin is installed at
124
+ def register_plugin(name, path)
125
+ commands = @commands
126
+
127
+ @commands = {}
128
+
129
+ begin
130
+ load path.join(PLUGIN_FILE_NAME), true
131
+ rescue StandardError => e
132
+ raise MalformattedPlugin, "#{e.class}: #{e.message}"
133
+ end
134
+
135
+ index.register_plugin name, path.to_s, @commands.keys
136
+ ensure
137
+ @commands = commands
138
+ end
139
+
140
+ # Executes the plugins.rb file
141
+ #
142
+ # @param [String] name of the plugin
143
+ def load_plugin(name)
144
+ # Need to ensure before this that plugin root where the rest of gems
145
+ # are installed to be on load path to support plugin deps. Currently not
146
+ # done to avoid conflicts
147
+ path = index.plugin_path(name)
148
+
149
+ load path.join(PLUGIN_FILE_NAME)
150
+ end
151
+
152
+ class << self
153
+ private :load_plugin, :register_plugin, :save_plugins, :validate_plugin!
154
+ end
155
+ end
156
+ end