devpack 0.1.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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: '0853562f5cbb43d560d36d6913f3e1a7bc44ffb49561183146b65a6c69c2f6bb'
4
+ data.tar.gz: 63cb7a8c6d3bed3bb8806c7a2b3dcd12a82728cfe177c7e7c8f4f6f64112b071
5
+ SHA512:
6
+ metadata.gz: b814b628151622a7780f74356d77e94528a1053eb14cac5334faa5fb2784ec0114773d0c641cde3091ba0b12dde8010ed74efbed2f457cea5581abb9fc95fbb1
7
+ data.tar.gz: 0cea088ec5ba97520eab7734813d972b03cab7332447e3ff3b375716c54bfa87ddcee5168e06fc99c5f469fc4d79a15b599b26d8488e0c8c926ad1871322d62c
@@ -0,0 +1,12 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
9
+
10
+ # rspec failure tracking
11
+ .rspec_status
12
+ Gemfile.lock
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
@@ -0,0 +1,32 @@
1
+ Metrics/BlockLength:
2
+ Exclude:
3
+ - "spec/**/*"
4
+
5
+ Layout/EmptyLinesAroundAttributeAccessor:
6
+ Enabled: true
7
+ Layout/SpaceAroundMethodCallOperator:
8
+ Enabled: true
9
+ Lint/DeprecatedOpenSSLConstant:
10
+ Enabled: true
11
+ Lint/MixedRegexpCaptureTypes:
12
+ Enabled: true
13
+ Lint/RaiseException:
14
+ Enabled: true
15
+ Lint/StructNewOverride:
16
+ Enabled: true
17
+ Style/ExponentialNotation:
18
+ Enabled: true
19
+ Style/HashEachMethods:
20
+ Enabled: true
21
+ Style/HashTransformKeys:
22
+ Enabled: true
23
+ Style/HashTransformValues:
24
+ Enabled: true
25
+ Style/RedundantFetchBlock:
26
+ Enabled: true
27
+ Style/RedundantRegexpCharacterClass:
28
+ Enabled: true
29
+ Style/RedundantRegexpEscape:
30
+ Enabled: true
31
+ Style/SlicingWithRange:
32
+ Enabled: true
data/Gemfile ADDED
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ source 'https://rubygems.org'
4
+
5
+ gemspec
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2020 Bob Farrell
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -0,0 +1,5 @@
1
+ .PHONY: test
2
+ test:
3
+ bundle exec rspec
4
+ bundle exec rubocop
5
+ bundle exec strong_versions
@@ -0,0 +1,48 @@
1
+ # Devpack
2
+
3
+ Conveniently load a set of gems to tailor your development environment without modifying your application's _Gemfile_. Configurable globally or per-project.
4
+
5
+ ## Installation
6
+
7
+ Add the gem to your `Gemfile`:
8
+
9
+ ```ruby
10
+ group :development, :test do
11
+ gem 'devpack', '~> 0.1.0'
12
+ end
13
+ ```
14
+
15
+ And rebuild your bundle:
16
+
17
+ ```bash
18
+ $ bundle install
19
+ ```
20
+
21
+ ## Usage
22
+
23
+ Create a file named `.devpack` in your project's directory (or any parent directory) containing a list of gems you wish to load:
24
+
25
+ ```
26
+ # .devpack
27
+ awesome_print
28
+ byebug
29
+ better_errors
30
+ ```
31
+
32
+ All listed gems will be automatically required.
33
+
34
+ It is recommended that the `.devpack` file is added to your `.gitignore`:
35
+
36
+ ```
37
+ # .gitignore
38
+ .devpack
39
+ ```
40
+
41
+ To disable _Devpack_ set the environment variable `DISABLE_DEVPACK` to any value:
42
+ ```bash
43
+ DISABLE_DEVPACK=1 bundle exec ruby myapp.rb
44
+ ```
45
+
46
+ ## License
47
+
48
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/gem_tasks'
4
+ require 'rspec/core/rake_task'
5
+
6
+ RSpec::Core::RakeTask.new(:spec)
7
+
8
+ task default: :spec
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require 'bundler/setup'
5
+ require 'devpack'
6
+
7
+ # You can add fixtures and/or initialization code here to make experimenting
8
+ # with your gem easier. You can also use a different console, if you like.
9
+
10
+ # (If you use this, don't forget to add pry to your Gemfile!)
11
+ # require "pry"
12
+ # Pry.start
13
+
14
+ require 'irb'
15
+ IRB.start(__FILE__)
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'lib/devpack/version'
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = 'devpack'
7
+ spec.version = Devpack::VERSION
8
+ spec.authors = ['Bob Farrell']
9
+ spec.email = ['git@bob.frl']
10
+
11
+ spec.summary = 'Conveniently tailor your development environment'
12
+ spec.description = 'Provide a list of gems to load in your own environment'
13
+ spec.homepage = 'https://github.com/bobf/devpack'
14
+ spec.license = 'MIT'
15
+ spec.required_ruby_version = Gem::Requirement.new('>= 2.3.0')
16
+
17
+ spec.metadata['homepage_uri'] = spec.homepage
18
+ spec.metadata['source_code_uri'] = spec.homepage
19
+ spec.metadata['changelog_uri'] = "#{spec.homepage}/blob/master/CHANGELOG.md"
20
+
21
+ spec.files = Dir.chdir(File.expand_path(__dir__)) do
22
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
23
+ end
24
+ spec.bindir = 'bin'
25
+ spec.executables = []
26
+ spec.require_paths = ['lib']
27
+
28
+ spec.add_development_dependency 'byebug', '~> 11.1'
29
+ spec.add_development_dependency 'rspec', '~> 3.9'
30
+ spec.add_development_dependency 'rspec-its', '~> 1.3'
31
+ spec.add_development_dependency 'rubocop', '~> 0.86.0'
32
+ spec.add_development_dependency 'strong_versions', '~> 0.4.4'
33
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rubygems'
4
+ require 'pathname'
5
+
6
+ require 'devpack/version'
7
+ require 'devpack/gems'
8
+ require 'devpack/gem_specification_context'
9
+
10
+ module Devpack
11
+ class Error < StandardError; end
12
+ end
13
+
14
+ Devpack::Gems.new('.').load unless ENV.key?('DISABLE_DEVPACK')
@@ -0,0 +1,65 @@
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
@@ -0,0 +1,135 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Devpack
4
+ # Parses .devpack file and provides/loads a list of specified gems
5
+ class Gems
6
+ FILENAME = '.devpack'
7
+ MAX_PARENTS = 100 # Avoid infinite loops (symlinks/weird file systems)
8
+
9
+ def initialize(path)
10
+ @load_path = $LOAD_PATH
11
+ @gem_home = Pathname.new(ENV['GEM_HOME'])
12
+ @path = Pathname.new(path)
13
+ end
14
+
15
+ def load
16
+ path = devpack_path
17
+ return [] if path.nil?
18
+
19
+ gems, time = timed { load_devpack(path) }
20
+ names = gems.map(&:first)
21
+ warn(loaded_message(path, gems, time))
22
+ names
23
+ end
24
+
25
+ private
26
+
27
+ def timed
28
+ start = Time.now.utc
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)
50
+ end
51
+
52
+ def load_gem(name)
53
+ # TODO: Decide what to do when Bundler is not defined.
54
+ # Do we want to support this scenario ?
55
+ update_load_path(name) if defined?(Bundler)
56
+ [name, Kernel.require(name)]
57
+ rescue LoadError
58
+ warn(failure_message(name))
59
+ nil
60
+ end
61
+
62
+ def parent_devpack_path
63
+ next_parent = @path.parent
64
+ loop.with_index(1) do |_, index|
65
+ break if index >= MAX_PARENTS
66
+
67
+ next_parent = next_parent.parent
68
+ break if next_parent == next_parent.parent
69
+
70
+ path = next_parent.join(FILENAME)
71
+ next unless File.exist?(path)
72
+
73
+ return path
74
+ end
75
+ end
76
+
77
+ def warn(message)
78
+ Kernel.warn("[devpack] #{message}")
79
+ end
80
+
81
+ def gems_glob
82
+ @gems_glob ||= Dir.glob(@gem_home.join('gems', '**', '*'))
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?
101
+
102
+ $LOAD_PATH.concat(require_paths(path, name))
103
+ end
104
+
105
+ def require_paths(gem_path, name)
106
+ gemspec_path = gem_path.join("#{name}.gemspec")
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) }
119
+ end
120
+
121
+ def failure_message(name)
122
+ base = "Failed to load `#{name}`"
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)."
133
+ end
134
+ end
135
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Devpack
4
+ VERSION = '0.1.0'
5
+ end
metadata ADDED
@@ -0,0 +1,131 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: devpack
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Bob Farrell
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2020-07-04 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: byebug
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '11.1'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '11.1'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rspec
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '3.9'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '3.9'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec-its
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '1.3'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '1.3'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rubocop
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: 0.86.0
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: 0.86.0
69
+ - !ruby/object:Gem::Dependency
70
+ name: strong_versions
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: 0.4.4
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: 0.4.4
83
+ description: Provide a list of gems to load in your own environment
84
+ email:
85
+ - git@bob.frl
86
+ executables: []
87
+ extensions: []
88
+ extra_rdoc_files: []
89
+ files:
90
+ - ".gitignore"
91
+ - ".rspec"
92
+ - ".rubocop.yml"
93
+ - Gemfile
94
+ - LICENSE.txt
95
+ - Makefile
96
+ - README.md
97
+ - Rakefile
98
+ - bin/console
99
+ - bin/setup
100
+ - devpack.gemspec
101
+ - lib/devpack.rb
102
+ - lib/devpack/gem_specification_context.rb
103
+ - lib/devpack/gems.rb
104
+ - lib/devpack/version.rb
105
+ homepage: https://github.com/bobf/devpack
106
+ licenses:
107
+ - MIT
108
+ metadata:
109
+ homepage_uri: https://github.com/bobf/devpack
110
+ source_code_uri: https://github.com/bobf/devpack
111
+ changelog_uri: https://github.com/bobf/devpack/blob/master/CHANGELOG.md
112
+ post_install_message:
113
+ rdoc_options: []
114
+ require_paths:
115
+ - lib
116
+ required_ruby_version: !ruby/object:Gem::Requirement
117
+ requirements:
118
+ - - ">="
119
+ - !ruby/object:Gem::Version
120
+ version: 2.3.0
121
+ required_rubygems_version: !ruby/object:Gem::Requirement
122
+ requirements:
123
+ - - ">="
124
+ - !ruby/object:Gem::Version
125
+ version: '0'
126
+ requirements: []
127
+ rubygems_version: 3.1.2
128
+ signing_key:
129
+ specification_version: 4
130
+ summary: Conveniently tailor your development environment
131
+ test_files: []