devpack 0.1.0 → 0.2.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +2 -0
- data/.rubocop.yml +22 -0
- data/CHANGELOG.md +41 -0
- data/Makefile +4 -0
- data/README.md +53 -9
- data/devpack.gemspec +2 -2
- data/lib/devpack.rb +37 -3
- data/lib/devpack/config.rb +58 -0
- data/lib/devpack/gem_glob.rb +44 -0
- data/lib/devpack/gem_spec.rb +68 -0
- data/lib/devpack/gems.rb +28 -101
- data/lib/devpack/initializers.rb +40 -0
- data/lib/devpack/messages.rb +36 -0
- data/lib/devpack/railtie.rb +8 -0
- data/lib/devpack/timeable.rb +10 -0
- data/lib/devpack/version.rb +1 -1
- metadata +14 -6
- data/lib/devpack/gem_specification_context.rb +0 -65
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9502f5bb5412be236b272450cf30b9410a37676ce9a8ff7a769e01e08ebedc0f
|
4
|
+
data.tar.gz: f96bfcfbd97d530ee6a93ff54a8fef02066e5206c468d50d0ef0e4e8501055bc
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b94fee5740d2a0a57db65fdb9533d63397f0a1200babc95ea48104789084eab958570d0e659ba76cae1bbfc65e00839ab822ba9ebe300e8edc66d7490d28f266
|
7
|
+
data.tar.gz: 8d05d1b3839e26b658357613fa6f5a3f2c2251aa939aa6ac68b509cc9733530dddf21024933c11b84e52894bd48c7290fc713307b1e1882c04bd1d0b477d7eb6
|
data/.gitignore
CHANGED
data/.rubocop.yml
CHANGED
@@ -2,6 +2,10 @@ Metrics/BlockLength:
|
|
2
2
|
Exclude:
|
3
3
|
- "spec/**/*"
|
4
4
|
|
5
|
+
AllCops:
|
6
|
+
Exclude:
|
7
|
+
- "spec/fixtures/**/*"
|
8
|
+
|
5
9
|
Layout/EmptyLinesAroundAttributeAccessor:
|
6
10
|
Enabled: true
|
7
11
|
Layout/SpaceAroundMethodCallOperator:
|
@@ -30,3 +34,21 @@ Style/RedundantRegexpEscape:
|
|
30
34
|
Enabled: true
|
31
35
|
Style/SlicingWithRange:
|
32
36
|
Enabled: true
|
37
|
+
Lint/DuplicateElsifCondition:
|
38
|
+
Enabled: true
|
39
|
+
Style/AccessorGrouping:
|
40
|
+
Enabled: true
|
41
|
+
Style/ArrayCoercion:
|
42
|
+
Enabled: true
|
43
|
+
Style/BisectedAttrAccessor:
|
44
|
+
Enabled: true
|
45
|
+
Style/CaseLikeIf:
|
46
|
+
Enabled: true
|
47
|
+
Style/HashAsLastArrayItem:
|
48
|
+
Enabled: true
|
49
|
+
Style/HashLikeCase:
|
50
|
+
Enabled: true
|
51
|
+
Style/RedundantAssignment:
|
52
|
+
Enabled: true
|
53
|
+
Style/RedundantFileExtensionInRequire:
|
54
|
+
Enabled: true
|
data/CHANGELOG.md
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
# Changelog
|
2
|
+
|
3
|
+
## 0.1.0
|
4
|
+
|
5
|
+
Core functionality implemented. Load a `.devpack` configuration file from current directory or, if not present, from a parent directory. Attempt to locate and `require` all listed gems.
|
6
|
+
|
7
|
+
## 0.1.1
|
8
|
+
|
9
|
+
Use `GEM_PATH` instead of `GEM_HOME` to locate gems.
|
10
|
+
|
11
|
+
Optimise load time by searching non-recursively in `/gems` directory (for each path listed in `GEM_PATH`).
|
12
|
+
|
13
|
+
Load latest version of gem by default. Allow specifying version with rubygems syntax `example:0.1.0`.
|
14
|
+
|
15
|
+
Permit comments in config file.
|
16
|
+
|
17
|
+
Use `Gem::Specification` to load "rendered" gemspec (i.e. the file created by rubygems when the gem is installed). This version of the gemspec will load very quickly so no need to do custom gemspec parsing any more. This also accounts for "missing" gemspecs.
|
18
|
+
|
19
|
+
## 0.1.2
|
20
|
+
|
21
|
+
Recursively include gem dependencies in `$LOAD_PATH` rather than assuming that any dependencies are already loaded.
|
22
|
+
|
23
|
+
Include original error message when warning that a gem was unable to be loaded.
|
24
|
+
|
25
|
+
## 0.1.3
|
26
|
+
|
27
|
+
Use a more appropriate method of identifying the latest version of a gem (use `Gem::Version` to sort matched gem paths).
|
28
|
+
|
29
|
+
Fix edge case where e.g. `pry-rails-0.1.0` was matching for `pry` due to naive match logic. Split on last dash instead of first (i.e. don't assume gems will not have a dash in their name; last dash separates gem name from version in directory name).
|
30
|
+
|
31
|
+
## 0.2.0
|
32
|
+
|
33
|
+
Add support for initializers. Files located in a `.devpack_initializers` directory will be loaded after gems configured in `.devpack` have been loaded. When using _Rails_ these files will be loaded using the `after_initialize` hook. Thanks to @joshmn for this idea: https://github.com/bobf/devpack/issues/1
|
34
|
+
|
35
|
+
Show full tracebacks of load errors when `DEVPACK_DEBUG` is set in environment.
|
36
|
+
|
37
|
+
Rename `DISABLE_DEVPACK` environment variable to `DEVPACK_DISABLE` for consistency.
|
38
|
+
|
39
|
+
## 0.2.1
|
40
|
+
|
41
|
+
Fully activate gem on load: add gem spec to `Gem.loaded_specs` and set instance variables `@loaded` and `@activated` to `true`. This mimics `Gem::Specification#activate` to ensure that anything that depends on these characteristics will function as normal.
|
data/Makefile
CHANGED
data/README.md
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# Devpack
|
2
2
|
|
3
|
-
|
3
|
+
Include a single gem in your `Gemfile` to allow developers to optionally include their preferred set of development gems without cluttering the `Gemfile`. Configurable globally or per-project.
|
4
4
|
|
5
5
|
## Installation
|
6
6
|
|
@@ -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.1
|
11
|
+
gem 'devpack', '~> 0.2.1'
|
12
12
|
end
|
13
13
|
```
|
14
14
|
|
@@ -20,27 +20,71 @@ $ bundle install
|
|
20
20
|
|
21
21
|
## Usage
|
22
22
|
|
23
|
-
Create a file named `.devpack` in your project's directory
|
23
|
+
Create a file named `.devpack` in your project's directory:
|
24
24
|
|
25
25
|
```
|
26
26
|
# .devpack
|
27
27
|
awesome_print
|
28
28
|
byebug
|
29
29
|
better_errors
|
30
|
+
|
31
|
+
# Optionally specify a version:
|
32
|
+
pry:0.13.1
|
33
|
+
```
|
34
|
+
|
35
|
+
All listed gems will be automatically required when _Devpack_ is loaded.
|
36
|
+
|
37
|
+
If your gems are not auto-loaded (e.g. by _Rails_) then you must require the gem:
|
38
|
+
```ruby
|
39
|
+
require 'devpack'
|
40
|
+
```
|
41
|
+
|
42
|
+
Any gems that fail to load (due to `LoadError`) will generate a warning.
|
43
|
+
|
44
|
+
It is recommended that `.devpack` is added to your `.gitignore`.
|
45
|
+
|
46
|
+
### Initializers
|
47
|
+
|
48
|
+
Custom initializers can be loaded by creating a directory named `.devpack_initializers` containing a set of `.rb` files.
|
49
|
+
|
50
|
+
Initializers will be loaded in alphabetical order after all gems listed in the `.devpack` configuration file have been loaded.
|
51
|
+
|
52
|
+
Initializers that fail to load (for any reason) will generate a warning.
|
53
|
+
|
54
|
+
```ruby
|
55
|
+
# .devpack_initializers/pry.rb
|
56
|
+
|
57
|
+
Pry.config.pager = false
|
30
58
|
```
|
31
59
|
|
32
|
-
|
60
|
+
#### Rails
|
33
61
|
|
34
|
-
|
62
|
+
If _Rails_ is detected then files in the `.devpack_initializers` directory will be loaded using the _Rails_ `after_initialize` hook (i.e. after all other frameworks have been initialized).
|
35
63
|
|
64
|
+
```ruby
|
65
|
+
# .devpack_initializers/bullet.rb
|
66
|
+
|
67
|
+
Bullet.enable = true
|
36
68
|
```
|
37
|
-
|
38
|
-
|
69
|
+
|
70
|
+
### Global Configuration
|
71
|
+
|
72
|
+
To configure globally simply save your `.devpack` configuration file to any parent directory of your project directory, e.g. `~/.devpack`.
|
73
|
+
|
74
|
+
This strategy also applies to `.devpack_initializers`.
|
75
|
+
|
76
|
+
### Disabling
|
77
|
+
|
78
|
+
To disable _Devpack_ set the environment variable `DEVPACK_DISABLE` to any value:
|
79
|
+
```bash
|
80
|
+
DEVPACK_DISABLE=1 bundle exec ruby myapp.rb
|
39
81
|
```
|
40
82
|
|
41
|
-
|
83
|
+
### Debugging
|
84
|
+
|
85
|
+
To see the full traceback of any errors encountered at load time set the environment variable `DEVPACK_DEBUG` to any value:
|
42
86
|
```bash
|
43
|
-
|
87
|
+
DEVPACK_DEBUG=1 bundle exec ruby myapp.rb
|
44
88
|
```
|
45
89
|
|
46
90
|
## License
|
data/devpack.gemspec
CHANGED
@@ -9,7 +9,7 @@ Gem::Specification.new do |spec|
|
|
9
9
|
spec.email = ['git@bob.frl']
|
10
10
|
|
11
11
|
spec.summary = 'Conveniently tailor your development environment'
|
12
|
-
spec.description = '
|
12
|
+
spec.description = 'Allow developers to optionally include a set of development gems without adding to the Gemfile.'
|
13
13
|
spec.homepage = 'https://github.com/bobf/devpack'
|
14
14
|
spec.license = 'MIT'
|
15
15
|
spec.required_ruby_version = Gem::Requirement.new('>= 2.3.0')
|
@@ -28,6 +28,6 @@ Gem::Specification.new do |spec|
|
|
28
28
|
spec.add_development_dependency 'byebug', '~> 11.1'
|
29
29
|
spec.add_development_dependency 'rspec', '~> 3.9'
|
30
30
|
spec.add_development_dependency 'rspec-its', '~> 1.3'
|
31
|
-
spec.add_development_dependency 'rubocop', '~> 0.
|
31
|
+
spec.add_development_dependency 'rubocop', '~> 0.88.0'
|
32
32
|
spec.add_development_dependency 'strong_versions', '~> 0.4.4'
|
33
33
|
end
|
data/lib/devpack.rb
CHANGED
@@ -3,12 +3,46 @@
|
|
3
3
|
require 'rubygems'
|
4
4
|
require 'pathname'
|
5
5
|
|
6
|
-
require 'devpack/
|
6
|
+
require 'devpack/timeable'
|
7
|
+
require 'devpack/config'
|
7
8
|
require 'devpack/gems'
|
8
|
-
require 'devpack/
|
9
|
+
require 'devpack/gem_glob'
|
10
|
+
require 'devpack/gem_spec'
|
11
|
+
require 'devpack/initializers'
|
12
|
+
require 'devpack/messages'
|
13
|
+
require 'devpack/version'
|
9
14
|
|
15
|
+
# Provides helper method for writing warning messages.
|
10
16
|
module Devpack
|
11
17
|
class Error < StandardError; end
|
18
|
+
|
19
|
+
class << self
|
20
|
+
def warn(message)
|
21
|
+
prefixed = message.split("\n").map { |line| "[devpack] #{line}" }.join("\n")
|
22
|
+
Kernel.warn(prefixed)
|
23
|
+
end
|
24
|
+
|
25
|
+
def debug?
|
26
|
+
ENV.key?('DEVPACK_DEBUG')
|
27
|
+
end
|
28
|
+
|
29
|
+
def disabled?
|
30
|
+
ENV.key?('DEVPACK_DISABLE')
|
31
|
+
end
|
32
|
+
|
33
|
+
def rails?
|
34
|
+
defined?(Rails::Railtie)
|
35
|
+
end
|
36
|
+
|
37
|
+
def config
|
38
|
+
@config ||= Devpack::Config.new(Dir.pwd)
|
39
|
+
end
|
40
|
+
end
|
12
41
|
end
|
13
42
|
|
14
|
-
|
43
|
+
unless Devpack.disabled?
|
44
|
+
require 'devpack/railtie' if Devpack.rails?
|
45
|
+
|
46
|
+
Devpack::Gems.new(Devpack.config).load
|
47
|
+
Devpack::Initializers.new(Devpack.config).load unless Devpack.rails?
|
48
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Devpack
|
4
|
+
# Locates and parses .devpack config file
|
5
|
+
class Config
|
6
|
+
FILENAME = '.devpack'
|
7
|
+
INITIALIZERS_DIRECTORY_NAME = '.devpack_initializers'
|
8
|
+
MAX_PARENTS = 100 # Avoid infinite loops (symlinks/weird file systems)
|
9
|
+
|
10
|
+
def initialize(pwd)
|
11
|
+
@pwd = Pathname.new(pwd)
|
12
|
+
end
|
13
|
+
|
14
|
+
def requested_gems
|
15
|
+
return nil if devpack_path.nil?
|
16
|
+
|
17
|
+
File.readlines(devpack_path)
|
18
|
+
.map(&filter_comments)
|
19
|
+
.compact
|
20
|
+
end
|
21
|
+
|
22
|
+
def devpack_path
|
23
|
+
@devpack_path ||= located_path(@pwd, FILENAME, :file)
|
24
|
+
end
|
25
|
+
|
26
|
+
def devpack_initializers_path
|
27
|
+
@devpack_initializers_path ||= located_path(@pwd, INITIALIZERS_DIRECTORY_NAME, :directory)
|
28
|
+
end
|
29
|
+
|
30
|
+
def devpack_initializer_paths
|
31
|
+
devpack_initializers_path&.glob(File.join('**', '*.rb'))&.map(&:to_s)&.sort || []
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def located_path(next_parent, filename, type)
|
37
|
+
loop.with_index(1) do |_, index|
|
38
|
+
return nil if index > MAX_PARENTS
|
39
|
+
|
40
|
+
path = next_parent.join(filename)
|
41
|
+
next_parent = next_parent.parent
|
42
|
+
next unless File.exist?(path) && File.public_send("#{type}?", path)
|
43
|
+
|
44
|
+
return path
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def filter_comments
|
49
|
+
proc do |line|
|
50
|
+
stripped = line.strip
|
51
|
+
next nil if stripped.empty?
|
52
|
+
next nil if stripped.start_with?('#')
|
53
|
+
|
54
|
+
stripped.gsub(/\s*#.*$/, '') # Remove inline comments (like this one)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Devpack
|
4
|
+
# Locates gems by searching in paths listed in GEM_PATH
|
5
|
+
class GemGlob
|
6
|
+
def find(name)
|
7
|
+
matched_paths(name).max { |a, b| version(a) <=> version(b) }
|
8
|
+
end
|
9
|
+
|
10
|
+
private
|
11
|
+
|
12
|
+
def glob
|
13
|
+
@glob ||= gem_paths.map { |path| Dir.glob(path.join('gems', '*')) }.flatten
|
14
|
+
end
|
15
|
+
|
16
|
+
def gem_paths
|
17
|
+
return [] unless ENV.key?('GEM_PATH')
|
18
|
+
|
19
|
+
ENV.fetch('GEM_PATH').split(':').map { |path| Pathname.new(path) }
|
20
|
+
end
|
21
|
+
|
22
|
+
def match?(name_with_version, basename)
|
23
|
+
name, _, version = name_with_version.partition(':')
|
24
|
+
return true if version.empty? && basename.rpartition('-').first == name
|
25
|
+
return true if !version.empty? && basename == "#{name}-#{version}"
|
26
|
+
|
27
|
+
false
|
28
|
+
end
|
29
|
+
|
30
|
+
def matched_paths(name)
|
31
|
+
glob.select do |path|
|
32
|
+
pathname = Pathname.new(path)
|
33
|
+
next unless pathname.directory?
|
34
|
+
|
35
|
+
basename = pathname.basename.to_s
|
36
|
+
match?(name, basename)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def version(path)
|
41
|
+
Gem::Version.new(File.split(path).last.rpartition('-').last)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Devpack
|
4
|
+
# Locates relevant gemspec for a given gem and provides a full list of paths
|
5
|
+
# for all `require_paths` listed in gemspec.
|
6
|
+
class GemSpec
|
7
|
+
def initialize(glob, name)
|
8
|
+
@name = name
|
9
|
+
@glob = glob
|
10
|
+
end
|
11
|
+
|
12
|
+
def require_paths(visited = Set.new)
|
13
|
+
return [] unless gemspec_path&.exist? && gem_path&.exist?
|
14
|
+
|
15
|
+
(immediate_require_paths + dependency_require_paths(visited))
|
16
|
+
.compact.flatten.uniq
|
17
|
+
end
|
18
|
+
|
19
|
+
def gemspec
|
20
|
+
@gemspec ||= Gem::Specification.load(gemspec_path.to_s)
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def dependency_require_paths(visited)
|
26
|
+
dependencies.map do |dependency|
|
27
|
+
next nil if visited.include?(dependency)
|
28
|
+
|
29
|
+
visited << dependency
|
30
|
+
GemSpec.new(@glob, name_with_version(dependency)).require_paths(visited)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def dependencies
|
35
|
+
gemspec.runtime_dependencies
|
36
|
+
end
|
37
|
+
|
38
|
+
def gem_path
|
39
|
+
return nil if located_gem.nil?
|
40
|
+
|
41
|
+
Pathname.new(located_gem)
|
42
|
+
end
|
43
|
+
|
44
|
+
def gemspec_path
|
45
|
+
return nil if gem_path.nil?
|
46
|
+
|
47
|
+
gem_path.join('..', '..', 'specifications', "#{gem_path.basename}.gemspec")
|
48
|
+
.expand_path
|
49
|
+
end
|
50
|
+
|
51
|
+
def immediate_require_paths
|
52
|
+
gemspec
|
53
|
+
.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
|
62
|
+
end
|
63
|
+
|
64
|
+
def located_gem
|
65
|
+
@located_gem ||= @glob.find(@name)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
data/lib/devpack/gems.rb
CHANGED
@@ -1,135 +1,62 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Devpack
|
4
|
-
#
|
4
|
+
# Loads requested gems from configuration
|
5
5
|
class Gems
|
6
|
-
|
7
|
-
MAX_PARENTS = 100 # Avoid infinite loops (symlinks/weird file systems)
|
6
|
+
include Timeable
|
8
7
|
|
9
|
-
def initialize(
|
10
|
-
@
|
11
|
-
@gem_home = Pathname.new(ENV['GEM_HOME'])
|
12
|
-
@path = Pathname.new(path)
|
8
|
+
def initialize(config)
|
9
|
+
@config = config
|
13
10
|
end
|
14
11
|
|
15
12
|
def load
|
16
|
-
|
17
|
-
return [] if path.nil?
|
13
|
+
return [] if @config.requested_gems.nil?
|
18
14
|
|
19
|
-
gems, time = timed { load_devpack
|
15
|
+
gems, time = timed { load_devpack }
|
20
16
|
names = gems.map(&:first)
|
21
|
-
warn(
|
17
|
+
warn(Messages.loaded(@config.devpack_path, gems, time.round(2)))
|
22
18
|
names
|
23
19
|
end
|
24
20
|
|
25
21
|
private
|
26
22
|
|
27
|
-
def
|
28
|
-
|
29
|
-
result = yield
|
30
|
-
[result, Time.now.utc - start]
|
31
|
-
end
|
32
|
-
|
33
|
-
def load_devpack(path)
|
34
|
-
gem_list(path).map { |name| load_gem(name) }.compact
|
35
|
-
end
|
36
|
-
|
37
|
-
def devpack_path
|
38
|
-
return default_devpack_path if File.exist?(default_devpack_path)
|
39
|
-
return parent_devpack_path unless parent_devpack_path.nil?
|
40
|
-
|
41
|
-
nil
|
42
|
-
end
|
43
|
-
|
44
|
-
def default_devpack_path
|
45
|
-
@path.join(FILENAME)
|
46
|
-
end
|
47
|
-
|
48
|
-
def gem_list(path)
|
49
|
-
File.readlines(path).map(&:chomp)
|
23
|
+
def load_devpack
|
24
|
+
@config.requested_gems.map { |name| load_gem(name) }.compact
|
50
25
|
end
|
51
26
|
|
52
27
|
def load_gem(name)
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
[name, Kernel.require(name)]
|
57
|
-
rescue LoadError
|
58
|
-
warn(failure_message(name))
|
28
|
+
[name, activate(name)]
|
29
|
+
rescue LoadError => e
|
30
|
+
warn(Messages.failure(name, load_error_message(e)))
|
59
31
|
nil
|
60
32
|
end
|
61
33
|
|
62
|
-
def
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
path = next_parent.join(FILENAME)
|
71
|
-
next unless File.exist?(path)
|
72
|
-
|
73
|
-
return path
|
74
|
-
end
|
34
|
+
def activate(name)
|
35
|
+
spec = GemSpec.new(gem_glob, name)
|
36
|
+
update_load_path(spec.require_paths)
|
37
|
+
loaded = Kernel.require(name)
|
38
|
+
Gem.loaded_specs[name] = spec.gemspec
|
39
|
+
spec.gemspec&.activated = true
|
40
|
+
spec.gemspec&.instance_variable_set(:@loaded, true)
|
41
|
+
loaded
|
75
42
|
end
|
76
43
|
|
77
44
|
def warn(message)
|
78
|
-
|
45
|
+
Devpack.warn(message)
|
79
46
|
end
|
80
47
|
|
81
|
-
def
|
82
|
-
|
83
|
-
end
|
84
|
-
|
85
|
-
def gem_path(name)
|
86
|
-
found = gems_glob.find do |path|
|
87
|
-
pathname = Pathname.new(path)
|
88
|
-
next unless pathname.directory?
|
89
|
-
|
90
|
-
# TODO: We should allow optionally specifying a version and default to loading
|
91
|
-
# the latest version available.
|
92
|
-
pathname.basename.to_s.start_with?("#{name}-")
|
93
|
-
end
|
94
|
-
|
95
|
-
found.nil? ? nil : Pathname.new(found)
|
96
|
-
end
|
97
|
-
|
98
|
-
def update_load_path(name)
|
99
|
-
path = gem_path(name)
|
100
|
-
return if path.nil?
|
48
|
+
def load_error_message(error)
|
49
|
+
return "(#{error.message})" unless Devpack.debug?
|
101
50
|
|
102
|
-
|
51
|
+
%[(#{error.message})\n#{error.backtrace.join("\n")}]
|
103
52
|
end
|
104
53
|
|
105
|
-
def
|
106
|
-
|
107
|
-
lib_path = gem_path.join('lib')
|
108
|
-
# REVIEW: Some gems don't have a .gemspec - need to understand how they are loaded.
|
109
|
-
# Use `/lib` for now if it exists as this will work for vast majority of cases.
|
110
|
-
return [lib_path] if !gemspec_path.exist? && lib_path.exist?
|
111
|
-
|
112
|
-
gemspec = File.read(gemspec_path.to_s)
|
113
|
-
GemSpecificationContext.class_eval(gemspec)
|
114
|
-
full_require_paths(gem_path)
|
115
|
-
end
|
116
|
-
|
117
|
-
def full_require_paths(base_path)
|
118
|
-
GemSpecificationContext.require_paths.map { |path| base_path.join(path) }
|
54
|
+
def gem_glob
|
55
|
+
@gem_glob ||= GemGlob.new
|
119
56
|
end
|
120
57
|
|
121
|
-
def
|
122
|
-
|
123
|
-
install = (defined?(Bundler) ? 'bundle exec ' : '') + "gem install #{name}"
|
124
|
-
"#{base}. Try `#{install}`"
|
125
|
-
end
|
126
|
-
|
127
|
-
def loaded_message(path, gems, time)
|
128
|
-
already_loaded = gems.size - gems.reject { |_, loaded| loaded }.size
|
129
|
-
base = "Loaded #{already_loaded} development gem(s) from '#{path}' in #{time} seconds"
|
130
|
-
return "#{base}." if already_loaded == gems.size
|
131
|
-
|
132
|
-
"#{base} (#{gems.size - already_loaded} gem(s) were already loaded by environment)."
|
58
|
+
def update_load_path(paths)
|
59
|
+
$LOAD_PATH.concat(paths)
|
133
60
|
end
|
134
61
|
end
|
135
62
|
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Devpack
|
4
|
+
# Loads requested initializers from configuration
|
5
|
+
class Initializers
|
6
|
+
include Timeable
|
7
|
+
|
8
|
+
def initialize(config)
|
9
|
+
@config = config
|
10
|
+
end
|
11
|
+
|
12
|
+
def load
|
13
|
+
initializers, time = timed { load_initializers }
|
14
|
+
path = @config.devpack_initializers_path
|
15
|
+
return if path.nil?
|
16
|
+
|
17
|
+
args = path, initializers, time.round(2)
|
18
|
+
Devpack.warn(Messages.loaded_initializers(*args))
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def load_initializers
|
24
|
+
@config.devpack_initializer_paths.map { |path| load_initializer(path) }
|
25
|
+
end
|
26
|
+
|
27
|
+
def load_initializer(path)
|
28
|
+
require path
|
29
|
+
rescue ScriptError, StandardError => e
|
30
|
+
Devpack.warn(Messages.initializer_failure(path, message(e)))
|
31
|
+
nil
|
32
|
+
end
|
33
|
+
|
34
|
+
def message(error)
|
35
|
+
return "(#{error.class.name} - #{error.message&.split("\n")&.first})" unless Devpack.debug?
|
36
|
+
|
37
|
+
%[(#{error.class.name})\n#{error.message}\n#{error.backtrace.join("\n")}]
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Devpack
|
4
|
+
# Generates output messages.
|
5
|
+
class Messages
|
6
|
+
class << self
|
7
|
+
def failure(name, error_message)
|
8
|
+
base = "Failed to load `#{name}`"
|
9
|
+
install = "bundle exec gem install #{name}"
|
10
|
+
"#{base}. Try `#{install}` #{error_message}"
|
11
|
+
end
|
12
|
+
|
13
|
+
def initializer_failure(path, error_message)
|
14
|
+
"Failed to load initializer `#{path}`: #{error_message}"
|
15
|
+
end
|
16
|
+
|
17
|
+
def loaded(path, gems, time)
|
18
|
+
already_loaded = gems.size - gems.reject { |_, loaded| loaded }.size
|
19
|
+
base = "Loaded #{already_loaded} development gem(s) from '#{path}' in #{time} seconds"
|
20
|
+
return "#{base}." if already_loaded == gems.size
|
21
|
+
|
22
|
+
"#{base} (#{gems.size - already_loaded} gem(s) were already loaded by environment)."
|
23
|
+
end
|
24
|
+
|
25
|
+
def loaded_initializers(path, initializers, time)
|
26
|
+
"Loaded #{initializers.compact.size} initializer(s) from '#{path}' in #{time} seconds"
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def indented(message)
|
32
|
+
message.split("\n").map { |line| " #{line}" }.join("\n")
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
data/lib/devpack/version.rb
CHANGED
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.1
|
4
|
+
version: 0.2.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Bob Farrell
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-
|
11
|
+
date: 2020-08-03 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: byebug
|
@@ -58,14 +58,14 @@ dependencies:
|
|
58
58
|
requirements:
|
59
59
|
- - "~>"
|
60
60
|
- !ruby/object:Gem::Version
|
61
|
-
version: 0.
|
61
|
+
version: 0.88.0
|
62
62
|
type: :development
|
63
63
|
prerelease: false
|
64
64
|
version_requirements: !ruby/object:Gem::Requirement
|
65
65
|
requirements:
|
66
66
|
- - "~>"
|
67
67
|
- !ruby/object:Gem::Version
|
68
|
-
version: 0.
|
68
|
+
version: 0.88.0
|
69
69
|
- !ruby/object:Gem::Dependency
|
70
70
|
name: strong_versions
|
71
71
|
requirement: !ruby/object:Gem::Requirement
|
@@ -80,7 +80,8 @@ dependencies:
|
|
80
80
|
- - "~>"
|
81
81
|
- !ruby/object:Gem::Version
|
82
82
|
version: 0.4.4
|
83
|
-
description:
|
83
|
+
description: Allow developers to optionally include a set of development gems without
|
84
|
+
adding to the Gemfile.
|
84
85
|
email:
|
85
86
|
- git@bob.frl
|
86
87
|
executables: []
|
@@ -90,6 +91,7 @@ files:
|
|
90
91
|
- ".gitignore"
|
91
92
|
- ".rspec"
|
92
93
|
- ".rubocop.yml"
|
94
|
+
- CHANGELOG.md
|
93
95
|
- Gemfile
|
94
96
|
- LICENSE.txt
|
95
97
|
- Makefile
|
@@ -99,8 +101,14 @@ files:
|
|
99
101
|
- bin/setup
|
100
102
|
- devpack.gemspec
|
101
103
|
- lib/devpack.rb
|
102
|
-
- lib/devpack/
|
104
|
+
- lib/devpack/config.rb
|
105
|
+
- lib/devpack/gem_glob.rb
|
106
|
+
- lib/devpack/gem_spec.rb
|
103
107
|
- lib/devpack/gems.rb
|
108
|
+
- lib/devpack/initializers.rb
|
109
|
+
- lib/devpack/messages.rb
|
110
|
+
- lib/devpack/railtie.rb
|
111
|
+
- lib/devpack/timeable.rb
|
104
112
|
- lib/devpack/version.rb
|
105
113
|
homepage: https://github.com/bobf/devpack
|
106
114
|
licenses:
|
@@ -1,65 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Devpack
|
4
|
-
# Stubbed instance_eval context to evaluate a .gemspec file and extract required_paths
|
5
|
-
# Avoids doing expensive operations (e.g. attempting to run `git ls-files`) which are
|
6
|
-
# typically invoked when using `Gem::Specification.load`.
|
7
|
-
class GemSpecificationContext
|
8
|
-
def initialize(*_); end
|
9
|
-
|
10
|
-
class << self
|
11
|
-
attr_accessor :require_paths
|
12
|
-
|
13
|
-
@require_paths = []
|
14
|
-
|
15
|
-
def require(*_); end
|
16
|
-
|
17
|
-
def __dir__
|
18
|
-
'.'
|
19
|
-
end
|
20
|
-
|
21
|
-
def require_relative(*_); end
|
22
|
-
end
|
23
|
-
|
24
|
-
module Gem
|
25
|
-
# Stubbed Rubygems Gem::Specification. Everything except `require_paths=` is a no-op.
|
26
|
-
# Catches errors for missing constants and attempts to set them in the class_eval context.
|
27
|
-
# Constants are set recursively by setting each constant to GemSpecificationContext.
|
28
|
-
class Specification
|
29
|
-
def initialize(*_)
|
30
|
-
GemSpecificationContext.require_paths = []
|
31
|
-
begin
|
32
|
-
yield self
|
33
|
-
rescue NameError => e
|
34
|
-
__devpack_resolved_receiver(e).const_set(e.name, GemSpecificationContext)
|
35
|
-
retry
|
36
|
-
end
|
37
|
-
end
|
38
|
-
|
39
|
-
def require_paths=(paths)
|
40
|
-
GemSpecificationContext.require_paths.concat(paths)
|
41
|
-
end
|
42
|
-
|
43
|
-
private
|
44
|
-
|
45
|
-
def __devpack_resolved_receiver(error)
|
46
|
-
error.receiver
|
47
|
-
rescue ArgumentError
|
48
|
-
GemSpecificationContext
|
49
|
-
end
|
50
|
-
|
51
|
-
# rubocop:disable Style/MethodMissingSuper
|
52
|
-
def method_missing(method_name, *args)
|
53
|
-
return self unless Kernel.respond_to?(method_name)
|
54
|
-
|
55
|
-
Kernel.public_send(method_name, *args)
|
56
|
-
end
|
57
|
-
# rubocop:enable Style/MethodMissingSuper
|
58
|
-
|
59
|
-
def respond_to_missing?(*_)
|
60
|
-
true
|
61
|
-
end
|
62
|
-
end
|
63
|
-
end
|
64
|
-
end
|
65
|
-
end
|