devpack 0.2.1 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
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: []