devpack 0.2.1 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9502f5bb5412be236b272450cf30b9410a37676ce9a8ff7a769e01e08ebedc0f
4
- data.tar.gz: f96bfcfbd97d530ee6a93ff54a8fef02066e5206c468d50d0ef0e4e8501055bc
3
+ metadata.gz: 9d17e8b2f27d6bf6dc15ff37e549e3f97a3550339daa6fba5db14174483c2197
4
+ data.tar.gz: e85c8dc654ae6fdf89c24ebaff90f0a14d2ae67b802b33a25430dde625c72a4b
5
5
  SHA512:
6
- metadata.gz: b94fee5740d2a0a57db65fdb9533d63397f0a1200babc95ea48104789084eab958570d0e659ba76cae1bbfc65e00839ab822ba9ebe300e8edc66d7490d28f266
7
- data.tar.gz: 8d05d1b3839e26b658357613fa6f5a3f2c2251aa939aa6ac68b509cc9733530dddf21024933c11b84e52894bd48c7290fc713307b1e1882c04bd1d0b477d7eb6
6
+ metadata.gz: 6095f9fe4d862df8a9a50ceccc1de6415f95a3e7a6749fd10076ff05e22244f343364e23231bb0270af7a1d0b9c02b5e627b82f49775c1cfa95cea10dc3f3c58
7
+ data.tar.gz: 79363c38a26853e07ef07ae3675ab867ce1d16bc52f1e31f6489ab1fd33e7cc09a45121a588c0b645a72a93c0adf1a0e5dfceb34424f0f1e5398d628e79f879e
data/README.md CHANGED
@@ -8,7 +8,7 @@ Add the gem to your `Gemfile`:
8
8
 
9
9
  ```ruby
10
10
  group :development, :test do
11
- gem 'devpack', '~> 0.2.1'
11
+ gem 'devpack', '~> 0.3.0'
12
12
  end
13
13
  ```
14
14
 
@@ -41,6 +41,8 @@ require 'devpack'
41
41
 
42
42
  Any gems that fail to load (due to `LoadError`) will generate a warning.
43
43
 
44
+ All dependencies are recursively verified for compatibily before loading. If no compatible version can be located then the gem will not be loaded.
45
+
44
46
  It is recommended that `.devpack` is added to your `.gitignore`.
45
47
 
46
48
  ### Initializers
@@ -2,6 +2,7 @@
2
2
 
3
3
  require 'rubygems'
4
4
  require 'pathname'
5
+ require 'set'
5
6
 
6
7
  require 'devpack/timeable'
7
8
  require 'devpack/config'
@@ -4,7 +4,9 @@ module Devpack
4
4
  # Locates gems by searching in paths listed in GEM_PATH
5
5
  class GemGlob
6
6
  def find(name)
7
- matched_paths(name).max { |a, b| version(a) <=> version(b) }
7
+ matched_paths(name)
8
+ .sort { |a, b| version(a) <=> version(b) }
9
+ .reverse
8
10
  end
9
11
 
10
12
  private
@@ -4,30 +4,68 @@ module Devpack
4
4
  # Locates relevant gemspec for a given gem and provides a full list of paths
5
5
  # for all `require_paths` listed in gemspec.
6
6
  class GemSpec
7
- def initialize(glob, name)
7
+ def initialize(glob, name, requirement)
8
8
  @name = name
9
9
  @glob = glob
10
+ @requirement = requirement
11
+ @dependency = Gem::Dependency.new(@name, @requirement)
10
12
  end
11
13
 
12
14
  def require_paths(visited = Set.new)
13
- return [] unless gemspec_path&.exist? && gem_path&.exist?
15
+ raise LoadError, Messages.no_compatible_version(@dependency) if gemspec.nil?
14
16
 
15
- (immediate_require_paths + dependency_require_paths(visited))
16
- .compact.flatten.uniq
17
+ (immediate_require_paths + dependency_require_paths(visited)).compact.flatten.uniq
17
18
  end
18
19
 
19
20
  def gemspec
20
- @gemspec ||= Gem::Specification.load(gemspec_path.to_s)
21
+ @gemspec ||= gemspecs.find do |spec|
22
+ next false if spec.nil?
23
+
24
+ @dependency.requirement.satisfied_by?(spec.version) && compatible?(spec)
25
+ end
21
26
  end
22
27
 
23
28
  private
24
29
 
30
+ def compatible?(spec)
31
+ return false if spec.nil?
32
+ return false if incompatible_version_loaded?(spec)
33
+
34
+ compatible_specs?(Gem.loaded_specs.values, [@dependency] + spec.runtime_dependencies)
35
+ end
36
+
37
+ def incompatible_version_loaded?(spec)
38
+ matched = Gem.loaded_specs[spec.name]
39
+ return false if matched.nil?
40
+
41
+ matched.version != spec.version
42
+ end
43
+
44
+ def compatible_specs?(specs, dependencies)
45
+ specs.all? { |spec| compatible_dependencies?(dependencies, spec) }
46
+ end
47
+
48
+ def compatible_dependencies?(dependencies, spec)
49
+ dependencies.all? { |dependency| compatible_dependency?(dependency, spec) }
50
+ end
51
+
52
+ def compatible_dependency?(dependency, spec)
53
+ return false if spec.nil?
54
+ return true unless dependency.name == spec.name
55
+
56
+ dependency.requirement.satisfied_by?(spec.version)
57
+ end
58
+
59
+ def gemspecs
60
+ @gemspecs ||= gemspec_paths.map { |path| Gem::Specification.load(path.to_s) }
61
+ end
62
+
25
63
  def dependency_require_paths(visited)
26
64
  dependencies.map do |dependency|
27
65
  next nil if visited.include?(dependency)
28
66
 
29
67
  visited << dependency
30
- GemSpec.new(@glob, name_with_version(dependency)).require_paths(visited)
68
+ GemSpec.new(@glob, dependency.name, dependency.requirement).require_paths(visited)
31
69
  end
32
70
  end
33
71
 
@@ -35,34 +73,28 @@ module Devpack
35
73
  gemspec.runtime_dependencies
36
74
  end
37
75
 
38
- def gem_path
39
- return nil if located_gem.nil?
76
+ def gem_paths
77
+ return nil if candidates.empty?
40
78
 
41
- Pathname.new(located_gem)
79
+ candidates.map { |candidate| Pathname.new(candidate) }
42
80
  end
43
81
 
44
- def gemspec_path
45
- return nil if gem_path.nil?
82
+ def gemspec_paths
83
+ return [] if gem_paths.nil?
46
84
 
47
- gem_path.join('..', '..', 'specifications', "#{gem_path.basename}.gemspec")
48
- .expand_path
85
+ gem_paths.map do |path|
86
+ path.join('..', '..', 'specifications', "#{path.basename}.gemspec").expand_path
87
+ end
49
88
  end
50
89
 
51
90
  def immediate_require_paths
52
91
  gemspec
53
92
  .require_paths
54
- .map { |path| gem_path.join(path).to_s }
55
- end
56
-
57
- def name_with_version(dependency)
58
- spec = dependency.to_spec
59
- "#{spec.name}:#{spec.version}"
60
- rescue Gem::MissingSpecError
61
- dependency.name
93
+ .map { |path| File.join(gemspec.full_gem_path, path) }
62
94
  end
63
95
 
64
- def located_gem
65
- @located_gem ||= @glob.find(@name)
96
+ def candidates
97
+ @candidates ||= @glob.find(@name)
66
98
  end
67
99
  end
68
100
  end
@@ -5,8 +5,9 @@ module Devpack
5
5
  class Gems
6
6
  include Timeable
7
7
 
8
- def initialize(config)
8
+ def initialize(config, glob = GemGlob.new)
9
9
  @config = config
10
+ @gem_glob = glob
10
11
  end
11
12
 
12
13
  def load
@@ -21,18 +22,21 @@ module Devpack
21
22
  private
22
23
 
23
24
  def load_devpack
24
- @config.requested_gems.map { |name| load_gem(name) }.compact
25
+ @config.requested_gems.map do |requested|
26
+ name, _, version = requested.partition(':')
27
+ load_gem(name, version.empty? ? nil : Gem::Requirement.new("= #{version}"))
28
+ end.compact
25
29
  end
26
30
 
27
- def load_gem(name)
28
- [name, activate(name)]
31
+ def load_gem(name, requirement)
32
+ [name, activate(name, requirement)]
29
33
  rescue LoadError => e
30
34
  warn(Messages.failure(name, load_error_message(e)))
31
35
  nil
32
36
  end
33
37
 
34
- def activate(name)
35
- spec = GemSpec.new(gem_glob, name)
38
+ def activate(name, version)
39
+ spec = GemSpec.new(@gem_glob, name, version)
36
40
  update_load_path(spec.require_paths)
37
41
  loaded = Kernel.require(name)
38
42
  Gem.loaded_specs[name] = spec.gemspec
@@ -51,10 +55,6 @@ module Devpack
51
55
  %[(#{error.message})\n#{error.backtrace.join("\n")}]
52
56
  end
53
57
 
54
- def gem_glob
55
- @gem_glob ||= GemGlob.new
56
- end
57
-
58
58
  def update_load_path(paths)
59
59
  $LOAD_PATH.concat(paths)
60
60
  end
@@ -6,8 +6,7 @@ module Devpack
6
6
  class << self
7
7
  def failure(name, error_message)
8
8
  base = "Failed to load `#{name}`"
9
- install = "bundle exec gem install #{name}"
10
- "#{base}. Try `#{install}` #{error_message}"
9
+ "#{base}. #{error_message}"
11
10
  end
12
11
 
13
12
  def initializer_failure(path, error_message)
@@ -26,6 +25,10 @@ module Devpack
26
25
  "Loaded #{initializers.compact.size} initializer(s) from '#{path}' in #{time} seconds"
27
26
  end
28
27
 
28
+ def no_compatible_version(dependency)
29
+ "No compatible version found for `#{dependency.requirement}`"
30
+ end
31
+
29
32
  private
30
33
 
31
34
  def indented(message)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Devpack
4
- VERSION = '0.2.1'
4
+ VERSION = '0.3.0'
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: devpack
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.1
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Bob Farrell
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-08-03 00:00:00.000000000 Z
11
+ date: 2020-09-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: byebug
@@ -117,7 +117,7 @@ metadata:
117
117
  homepage_uri: https://github.com/bobf/devpack
118
118
  source_code_uri: https://github.com/bobf/devpack
119
119
  changelog_uri: https://github.com/bobf/devpack/blob/master/CHANGELOG.md
120
- post_install_message:
120
+ post_install_message:
121
121
  rdoc_options: []
122
122
  require_paths:
123
123
  - lib
@@ -133,7 +133,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
133
133
  version: '0'
134
134
  requirements: []
135
135
  rubygems_version: 3.1.2
136
- signing_key:
136
+ signing_key:
137
137
  specification_version: 4
138
138
  summary: Conveniently tailor your development environment
139
139
  test_files: []