devpack 0.3.0 → 0.4.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: 9d17e8b2f27d6bf6dc15ff37e549e3f97a3550339daa6fba5db14174483c2197
4
- data.tar.gz: e85c8dc654ae6fdf89c24ebaff90f0a14d2ae67b802b33a25430dde625c72a4b
3
+ metadata.gz: 88ec4f3a9263a0ce2204ff40a97c9f502897ad2eab40d5ec9356dcab0dccdd10
4
+ data.tar.gz: 5e6e9173e18339fa091cda8780588b0378c95c9589f8344a328d2e368f6649fa
5
5
  SHA512:
6
- metadata.gz: 6095f9fe4d862df8a9a50ceccc1de6415f95a3e7a6749fd10076ff05e22244f343364e23231bb0270af7a1d0b9c02b5e627b82f49775c1cfa95cea10dc3f3c58
7
- data.tar.gz: 79363c38a26853e07ef07ae3675ab867ce1d16bc52f1e31f6489ab1fd33e7cc09a45121a588c0b645a72a93c0adf1a0e5dfceb34424f0f1e5398d628e79f879e
6
+ metadata.gz: 99bce1bb4528c5a2da45d6165481a05334688dec0c9a5f8bd6ac8778e4a85ca3dca45331134881a2d57b4bf0a664e1341541158456b48dde40e264dce7badcea
7
+ data.tar.gz: 693c4a80282a14cc25a4be573d8b3de84823083251e3a8417b6c056edcba1d5e3170700962d86ef5dccb1508b8185fd9f124fc138d46af9c4ac3e175a164edef
data/.rubocop.yml CHANGED
@@ -3,52 +3,10 @@ Metrics/BlockLength:
3
3
  - "spec/**/*"
4
4
 
5
5
  AllCops:
6
+ NewCops: enable
6
7
  Exclude:
7
8
  - "spec/fixtures/**/*"
8
9
 
9
- Layout/EmptyLinesAroundAttributeAccessor:
10
- Enabled: true
11
- Layout/SpaceAroundMethodCallOperator:
12
- Enabled: true
13
- Lint/DeprecatedOpenSSLConstant:
14
- Enabled: true
15
- Lint/MixedRegexpCaptureTypes:
16
- Enabled: true
17
- Lint/RaiseException:
18
- Enabled: true
19
- Lint/StructNewOverride:
20
- Enabled: true
21
- Style/ExponentialNotation:
22
- Enabled: true
23
- Style/HashEachMethods:
24
- Enabled: true
25
- Style/HashTransformKeys:
26
- Enabled: true
27
- Style/HashTransformValues:
28
- Enabled: true
29
- Style/RedundantFetchBlock:
30
- Enabled: true
31
- Style/RedundantRegexpCharacterClass:
32
- Enabled: true
33
- Style/RedundantRegexpEscape:
34
- Enabled: true
35
- Style/SlicingWithRange:
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
10
+ Bundler/GemFilename:
11
+ Exclude:
12
+ - "lib/devpack/gems.rb"
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 2.5.8
data/README.md CHANGED
@@ -4,46 +4,54 @@ Include a single gem in your `Gemfile` to allow developers to optionally include
4
4
 
5
5
  ## Installation
6
6
 
7
- Add the gem to your `Gemfile`:
7
+ Create a file named `.devpack` in your project's directory, or in any parent directory:
8
+
9
+ ```
10
+ # .devpack
11
+ awesome_print
12
+ byebug
13
+ better_errors
14
+
15
+ # Optionally specify a version:
16
+ pry:0.13.1
17
+ ```
18
+
19
+ Add _Devpack_ to any project's `Gemfile`:
8
20
 
9
21
  ```ruby
22
+ # Gemfile
10
23
  group :development, :test do
11
- gem 'devpack', '~> 0.3.0'
24
+ gem 'devpack', '~> 0.4.0'
12
25
  end
13
26
  ```
14
27
 
15
- And rebuild your bundle:
28
+ Rebuild your bundle:
16
29
 
17
30
  ```bash
18
- $ bundle install
31
+ bundle install
19
32
  ```
20
33
 
21
34
  ## Usage
22
35
 
23
- Create a file named `.devpack` in your project's directory:
36
+ Load _Devpack_ (if your gems are not auto-loaded as in e.g. a _Rails_ application environment):
24
37
 
38
+ ```ruby
39
+ require 'devpack'
25
40
  ```
26
- # .devpack
27
- awesome_print
28
- byebug
29
- better_errors
30
41
 
31
- # Optionally specify a version:
32
- pry:0.13.1
33
- ```
42
+ _Devpack_ will attempt to load all configured gems immediately, providing feedback to _stderr_. All dependencies are loaded with `require` after being recursively verified for compatibily with bundled gems before loading.
34
43
 
35
- All listed gems will be automatically required when _Devpack_ is loaded.
44
+ It is recommended to use a [global configuration](#global-configuration).
36
45
 
37
- If your gems are not auto-loaded (e.g. by _Rails_) then you must require the gem:
38
- ```ruby
39
- require 'devpack'
40
- ```
46
+ When using a per-project configuration, `.devpack` files should be added to `.gitignore`.
41
47
 
42
- Any gems that fail to load (due to `LoadError`) will generate a warning.
48
+ ### Gem Installation
43
49
 
44
- All dependencies are recursively verified for compatibily before loading. If no compatible version can be located then the gem will not be loaded.
50
+ A convenience command is provided to install all gems listed in `.devpack` file that are not already installed:
45
51
 
46
- It is recommended that `.devpack` is added to your `.gitignore`.
52
+ ```ruby
53
+ bundle exec devpack install
54
+ ```
47
55
 
48
56
  ### Initializers
49
57
 
@@ -70,11 +78,18 @@ Bullet.enable = true
70
78
  ```
71
79
 
72
80
  ### Global Configuration
73
-
81
+ <a name="global-configuration"></a>
74
82
  To configure globally simply save your `.devpack` configuration file to any parent directory of your project directory, e.g. `~/.devpack`.
75
83
 
76
84
  This strategy also applies to `.devpack_initializers`.
77
85
 
86
+ ### Silencing
87
+
88
+ To prevent _Devpack_ from displaying messages on load, set the environment variable `DEVPACK_SILENT=1` to any value:
89
+ ```bash
90
+ DEVPACK_SILENT=1 bundle exec ruby myapp.rb
91
+ ```
92
+
78
93
  ### Disabling
79
94
 
80
95
  To disable _Devpack_ set the environment variable `DEVPACK_DISABLE` to any value:
data/devpack.gemspec CHANGED
@@ -12,22 +12,24 @@ Gem::Specification.new do |spec|
12
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
- spec.required_ruby_version = Gem::Requirement.new('>= 2.3.0')
15
+ spec.required_ruby_version = Gem::Requirement.new('>= 2.5.0')
16
16
 
17
17
  spec.metadata['homepage_uri'] = spec.homepage
18
18
  spec.metadata['source_code_uri'] = spec.homepage
19
19
  spec.metadata['changelog_uri'] = "#{spec.homepage}/blob/master/CHANGELOG.md"
20
+ spec.metadata['rubygems_mfa_required'] = 'true'
20
21
 
21
22
  spec.files = Dir.chdir(File.expand_path(__dir__)) do
22
23
  `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
23
24
  end
24
- spec.bindir = 'bin'
25
- spec.executables = []
25
+ spec.bindir = 'exe'
26
+ spec.executables = ['devpack']
26
27
  spec.require_paths = ['lib']
27
28
 
28
29
  spec.add_development_dependency 'byebug', '~> 11.1'
29
30
  spec.add_development_dependency 'rspec', '~> 3.9'
30
31
  spec.add_development_dependency 'rspec-its', '~> 1.3'
31
- spec.add_development_dependency 'rubocop', '~> 0.88.0'
32
+ spec.add_development_dependency 'rubocop', '~> 1.8'
33
+ spec.add_development_dependency 'rubocop-rspec', '~> 2.1'
32
34
  spec.add_development_dependency 'strong_versions', '~> 0.4.4'
33
35
  end
data/exe/devpack ADDED
@@ -0,0 +1,32 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ ENV['DEVPACK_DISABLE'] = '1'
5
+
6
+ require 'open3'
7
+
8
+ require 'devpack'
9
+
10
+ command = ARGV[0]
11
+
12
+ if command == 'install'
13
+ missing = Devpack::Gems.new(Devpack.config).tap { |gems| gems.load(silent: true) }.missing
14
+ install_command = "bundle exec gem install -V #{missing.map(&:required_version).join(' ')}" unless missing.empty?
15
+ if install_command.nil?
16
+ warn '[devpack] No gems to install.'
17
+ else
18
+ warn "[devpack] [exec] #{install_command}"
19
+ output, status = Open3.capture2e(install_command)
20
+ puts output
21
+ puts status
22
+ if status.success?
23
+ warn '[devpack] Installation complete.'
24
+ else
25
+ warn "[devpack] Installation failed. Manually verify this command: #{install_command}"
26
+ end
27
+ end
28
+ exit 0
29
+ end
30
+
31
+ warn "[devpack] Unknown command: #{command}"
32
+ exit 1
@@ -16,9 +16,9 @@ module Devpack
16
16
  end
17
17
 
18
18
  def gem_paths
19
- return [] unless ENV.key?('GEM_PATH')
19
+ return [] if gem_path.nil?
20
20
 
21
- ENV.fetch('GEM_PATH').split(':').map { |path| Pathname.new(path) }
21
+ gem_path.split(':').map { |path| Pathname.new(path) }
22
22
  end
23
23
 
24
24
  def match?(name_with_version, basename)
@@ -42,5 +42,12 @@ module Devpack
42
42
  def version(path)
43
43
  Gem::Version.new(File.split(path).last.rpartition('-').last)
44
44
  end
45
+
46
+ def gem_path
47
+ return ENV['GEM_PATH'] if ENV.key?('GEM_PATH')
48
+ return ENV['GEM_HOME'] if ENV.key?('GEM_HOME')
49
+
50
+ nil
51
+ end
45
52
  end
46
53
  end
@@ -3,52 +3,104 @@
3
3
  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
+ # rubocop:disable Metrics/ClassLength
6
7
  class GemSpec
7
- def initialize(glob, name, requirement)
8
+ attr_reader :name, :root
9
+
10
+ def initialize(glob, name, requirement, root: nil)
8
11
  @name = name
9
12
  @glob = glob
10
13
  @requirement = requirement
14
+ @root = root || self
11
15
  @dependency = Gem::Dependency.new(@name, @requirement)
12
16
  end
13
17
 
14
18
  def require_paths(visited = Set.new)
15
- raise LoadError, Messages.no_compatible_version(@dependency) if gemspec.nil?
19
+ raise GemNotFoundError.new("Gem not found: #{required_version}", self) if gemspec.nil?
16
20
 
17
21
  (immediate_require_paths + dependency_require_paths(visited)).compact.flatten.uniq
18
22
  end
19
23
 
20
24
  def gemspec
25
+ return Gem.loaded_specs[@name] if compatible?(Gem.loaded_specs[@name])
26
+
21
27
  @gemspec ||= gemspecs.find do |spec|
22
28
  next false if spec.nil?
23
29
 
24
- @dependency.requirement.satisfied_by?(spec.version) && compatible?(spec)
30
+ raise_incompatible(spec) unless compatible?(spec)
31
+
32
+ @dependency.requirement.satisfied_by?(spec.version)
25
33
  end
26
34
  end
27
35
 
36
+ def pretty_name
37
+ return @name.to_s if @requirement.nil?
38
+
39
+ "#{@name} #{@requirement}"
40
+ end
41
+
42
+ def root?
43
+ self == @root
44
+ end
45
+
46
+ def required_version
47
+ return @name.to_s if compatible_spec.nil? && @requirement.nil?
48
+ return "#{@name}:#{compatible_version}" if compatible_spec.nil?
49
+
50
+ "#{@name}:#{compatible_spec.version}"
51
+ end
52
+
28
53
  private
29
54
 
30
55
  def compatible?(spec)
31
56
  return false if spec.nil?
32
57
  return false if incompatible_version_loaded?(spec)
33
58
 
34
- compatible_specs?(Gem.loaded_specs.values, [@dependency] + spec.runtime_dependencies)
59
+ compatible_specs?([@dependency] + spec.runtime_dependencies)
60
+ end
61
+
62
+ def compatible_spec
63
+ @compatible_spec ||= gemspecs.compact
64
+ .select { |spec| requirements_satisfied_by?(spec.version) }
65
+ .max_by(&:version)
35
66
  end
36
67
 
37
68
  def incompatible_version_loaded?(spec)
38
69
  matched = Gem.loaded_specs[spec.name]
39
70
  return false if matched.nil?
40
71
 
41
- matched.version != spec.version
72
+ !matched.satisfies_requirement?(@dependency)
73
+ end
74
+
75
+ def raise_incompatible(spec)
76
+ raise GemIncompatibilityError.new('Incompatible dependencies', incompatible_dependencies(spec))
77
+ end
78
+
79
+ def compatible_version
80
+ @requirement.requirements.map(&:last).max_by { |version| @requirement.satisfied_by?(version) }
42
81
  end
43
82
 
44
- def compatible_specs?(specs, dependencies)
45
- specs.all? { |spec| compatible_dependencies?(dependencies, spec) }
83
+ def requirements_satisfied_by?(version)
84
+ @dependency.requirement.satisfied_by?(version)
85
+ end
86
+
87
+ def compatible_specs?(dependencies)
88
+ Gem.loaded_specs.values.all? { |spec| compatible_dependencies?(dependencies, spec) }
46
89
  end
47
90
 
48
91
  def compatible_dependencies?(dependencies, spec)
49
92
  dependencies.all? { |dependency| compatible_dependency?(dependency, spec) }
50
93
  end
51
94
 
95
+ def incompatible_dependencies(spec)
96
+ dependencies = [@dependency] + spec.runtime_dependencies
97
+ Gem.loaded_specs.map do |_name, loaded_spec|
98
+ next nil if compatible_dependencies?(dependencies, loaded_spec)
99
+
100
+ [@root, dependencies.reject { |dependency| compatible_dependency?(dependency, loaded_spec) }]
101
+ end.compact
102
+ end
103
+
52
104
  def compatible_dependency?(dependency, spec)
53
105
  return false if spec.nil?
54
106
  return true unless dependency.name == spec.name
@@ -65,7 +117,7 @@ module Devpack
65
117
  next nil if visited.include?(dependency)
66
118
 
67
119
  visited << dependency
68
- GemSpec.new(@glob, dependency.name, dependency.requirement).require_paths(visited)
120
+ GemSpec.new(@glob, dependency.name, dependency.requirement, root: @root).require_paths(visited)
69
121
  end
70
122
  end
71
123
 
@@ -97,4 +149,5 @@ module Devpack
97
149
  @candidates ||= @glob.find(@name)
98
150
  end
99
151
  end
152
+ # rubocop:enable Metrics/ClassLength
100
153
  end
data/lib/devpack/gems.rb CHANGED
@@ -5,48 +5,70 @@ module Devpack
5
5
  class Gems
6
6
  include Timeable
7
7
 
8
+ attr_reader :missing
9
+
8
10
  def initialize(config, glob = GemGlob.new)
9
11
  @config = config
10
12
  @gem_glob = glob
13
+ @failures = []
14
+ @missing = []
15
+ @incompatible = []
11
16
  end
12
17
 
13
- def load
18
+ def load(silent: false)
14
19
  return [] if @config.requested_gems.nil?
15
20
 
16
21
  gems, time = timed { load_devpack }
17
22
  names = gems.map(&:first)
18
- warn(Messages.loaded(@config.devpack_path, gems, time.round(2)))
23
+ summarize(gems, time) unless silent
19
24
  names
20
25
  end
21
26
 
22
27
  private
23
28
 
29
+ def summarize(gems, time)
30
+ @failures.each { |failure| warn(:error, Messages.failure(failure[:name], failure[:message])) }
31
+ warn(:success, Messages.loaded(@config, gems, time.round(2)))
32
+ warn(:info, Messages.install_missing(@missing)) unless @missing.empty?
33
+ warn(:info, Messages.alert_incompatible(@incompatible.flatten(1))) unless @incompatible.empty?
34
+ end
35
+
24
36
  def load_devpack
25
37
  @config.requested_gems.map do |requested|
26
38
  name, _, version = requested.partition(':')
27
- load_gem(name, version.empty? ? nil : Gem::Requirement.new("= #{version}"))
39
+ load_gem(name, version.empty? ? nil : Gem::Requirement.new(version))
28
40
  end.compact
29
41
  end
30
42
 
31
43
  def load_gem(name, requirement)
32
44
  [name, activate(name, requirement)]
33
45
  rescue LoadError => e
34
- warn(Messages.failure(name, load_error_message(e)))
35
- nil
46
+ deactivate(name)
47
+ nil.tap { @failures << { name: name, message: load_error_message(e) } }
48
+ rescue GemNotFoundError => e
49
+ nil.tap { @missing << e.meta }
50
+ rescue GemIncompatibilityError => e
51
+ nil.tap { @incompatible << e.meta }
36
52
  end
37
53
 
38
54
  def activate(name, version)
39
55
  spec = GemSpec.new(@gem_glob, name, version)
40
56
  update_load_path(spec.require_paths)
41
- loaded = Kernel.require(name)
57
+ # NOTE: do this before we require, because some gems use the gemspec to
58
+ # declare their version...
42
59
  Gem.loaded_specs[name] = spec.gemspec
60
+ loaded = Kernel.require(name)
43
61
  spec.gemspec&.activated = true
44
62
  spec.gemspec&.instance_variable_set(:@loaded, true)
45
63
  loaded
46
64
  end
47
65
 
48
- def warn(message)
49
- Devpack.warn(message)
66
+ def deactivate(name)
67
+ Gem.loaded_specs.delete(name)
68
+ end
69
+
70
+ def warn(level, message)
71
+ Devpack.warn(level, message)
50
72
  end
51
73
 
52
74
  def load_error_message(error)
@@ -15,7 +15,7 @@ module Devpack
15
15
  return if path.nil?
16
16
 
17
17
  args = path, initializers, time.round(2)
18
- Devpack.warn(Messages.loaded_initializers(*args))
18
+ Devpack.warn(:success, Messages.loaded_initializers(*args))
19
19
  end
20
20
 
21
21
  private
@@ -27,7 +27,7 @@ module Devpack
27
27
  def load_initializer(path)
28
28
  require path
29
29
  rescue ScriptError, StandardError => e
30
- Devpack.warn(Messages.initializer_failure(path, message(e)))
30
+ Devpack.warn(:error, Messages.initializer_failure(path, message(e)))
31
31
  nil
32
32
  end
33
33
 
@@ -13,20 +13,52 @@ module Devpack
13
13
  "Failed to load initializer `#{path}`: #{error_message}"
14
14
  end
15
15
 
16
- def loaded(path, gems, time)
17
- already_loaded = gems.size - gems.reject { |_, loaded| loaded }.size
18
- base = "Loaded #{already_loaded} development gem(s) from '#{path}' in #{time} seconds"
19
- return "#{base}." if already_loaded == gems.size
16
+ # rubocop:disable Metrics/AbcSize
17
+ def loaded(config, gems, time)
18
+ loaded = gems.size - gems.reject { |_, devpack_loaded| devpack_loaded }.size
19
+ of_total = gems.size == config.requested_gems.size ? nil : " of #{color(:cyan) { config.requested_gems.size }}"
20
+ path = color(:cyan) { config.devpack_path }
21
+ base = "Loaded #{color(:green) { loaded }}#{of_total} development gem(s) from #{path} in #{time} seconds"
22
+ return "#{base}." if loaded == gems.size
20
23
 
21
- "#{base} (#{gems.size - already_loaded} gem(s) were already loaded by environment)."
24
+ "#{base} (#{color(:cyan) { gems.size - loaded }} gem(s) were already loaded by environment)."
22
25
  end
26
+ # rubocop:enable Metrics/AbcSize
23
27
 
24
28
  def loaded_initializers(path, initializers, time)
25
- "Loaded #{initializers.compact.size} initializer(s) from '#{path}' in #{time} seconds"
29
+ "Loaded #{color(:green) { initializers.compact.size }} initializer(s) from '#{path}' in #{time} seconds"
26
30
  end
27
31
 
28
- def no_compatible_version(dependency)
29
- "No compatible version found for `#{dependency.requirement}`"
32
+ def install_missing(missing)
33
+ command = color(:cyan) { 'bundle exec devpack install' }
34
+ grouped_missing = missing
35
+ .group_by(&:root)
36
+ .map do |root, dependencies|
37
+ next (color(:cyan) { root.pretty_name }).to_s if dependencies.all?(&:root?)
38
+
39
+ formatted_dependencies = dependencies.map { |dependency| color(:yellow) { dependency.pretty_name } }
40
+ "#{color(:cyan) { root.pretty_name }}: #{formatted_dependencies.join(', ')}"
41
+ end
42
+ "Install #{missing.size} missing gem(s): #{command} # [#{grouped_missing.join(', ')}]"
43
+ end
44
+
45
+ def alert_incompatible(incompatible)
46
+ grouped_dependencies = {}
47
+ incompatible.each do |spec, dependencies|
48
+ key = spec.root.pretty_name
49
+ grouped_dependencies[key] ||= []
50
+ grouped_dependencies[key] << dependencies
51
+ end
52
+ alert_incompatible_message(grouped_dependencies)
53
+ end
54
+
55
+ def test
56
+ puts "#{color(:green) { 'green' }} #{color(:red) { 'red' }} #{color(:blue) { 'blue' }}"
57
+ puts "#{color(:cyan) { 'cyan' }} #{color(:yellow) { 'yellow' }} #{color(:magenta) { 'magenta' }}"
58
+ end
59
+
60
+ def color(name)
61
+ "#{palette.fetch(name)}#{yield}#{palette.fetch(:reset)}"
30
62
  end
31
63
 
32
64
  private
@@ -34,6 +66,31 @@ module Devpack
34
66
  def indented(message)
35
67
  message.split("\n").map { |line| " #{line}" }.join("\n")
36
68
  end
69
+
70
+ def command(gems)
71
+ "bundle exec gem install #{gems.join(' ')}"
72
+ end
73
+
74
+ def alert_incompatible_message(grouped_dependencies)
75
+ incompatible_dependencies = grouped_dependencies.sort.map do |name, dependencies|
76
+ "#{color(:cyan) { name }}: "\
77
+ "#{dependencies.flatten.map { |dependency| color(:yellow) { dependency.to_s } }.join(', ')}"
78
+ end
79
+ "Unable to resolve version conflicts for #{color(:yellow) { incompatible_dependencies.size }} "\
80
+ "dependencies: #{incompatible_dependencies.join(', ')}}"
81
+ end
82
+
83
+ def palette
84
+ {
85
+ reset: "\e[39m",
86
+ red: "\e[31m",
87
+ green: "\e[32m",
88
+ yellow: "\e[33m",
89
+ blue: "\e[34m",
90
+ magenta: "\e[35m",
91
+ cyan: "\e[36m"
92
+ }
93
+ end
37
94
  end
38
95
  end
39
96
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Devpack
4
- VERSION = '0.3.0'
4
+ VERSION = '0.4.0'
5
5
  end
data/lib/devpack.rb CHANGED
@@ -15,11 +15,25 @@ require 'devpack/version'
15
15
 
16
16
  # Provides helper method for writing warning messages.
17
17
  module Devpack
18
- class Error < StandardError; end
18
+ # Base class for all Devpack errors. Accepts additional argument `meta` to store object info.
19
+ class Error < StandardError
20
+ attr_reader :message, :meta
21
+
22
+ def initialize(message = nil, meta = nil)
23
+ @message = message
24
+ @meta = meta
25
+ super(message)
26
+ end
27
+ end
28
+
29
+ class GemNotFoundError < Error; end
30
+ class GemIncompatibilityError < Error; end
19
31
 
20
32
  class << self
21
- def warn(message)
22
- prefixed = message.split("\n").map { |line| "[devpack] #{line}" }.join("\n")
33
+ def warn(level, message)
34
+ return if silent?
35
+
36
+ prefixed = message.split("\n").map { |line| "#{prefix(level)} #{line}" }.join("\n")
23
37
  Kernel.warn(prefixed)
24
38
  end
25
39
 
@@ -31,6 +45,10 @@ module Devpack
31
45
  ENV.key?('DEVPACK_DISABLE')
32
46
  end
33
47
 
48
+ def silent?
49
+ ENV.key?('DEVPACK_SILENT')
50
+ end
51
+
34
52
  def rails?
35
53
  defined?(Rails::Railtie)
36
54
  end
@@ -38,6 +56,20 @@ module Devpack
38
56
  def config
39
57
  @config ||= Devpack::Config.new(Dir.pwd)
40
58
  end
59
+
60
+ private
61
+
62
+ def prefix(level)
63
+ "#{Messages.color(:blue) { '[' }}devpack#{Messages.color(:blue) { ']' }} #{icon(level)}"
64
+ end
65
+
66
+ def icon(level)
67
+ {
68
+ success: Messages.color(:green) { '✓' },
69
+ info: Messages.color(:cyan) { 'ℹ' },
70
+ error: Messages.color(:red) { '✗' }
71
+ }.fetch(level)
72
+ end
41
73
  end
42
74
  end
43
75
 
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.3.0
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Bob Farrell
8
8
  autorequire:
9
- bindir: bin
9
+ bindir: exe
10
10
  cert_chain: []
11
- date: 2020-09-05 00:00:00.000000000 Z
11
+ date: 2021-12-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: byebug
@@ -58,14 +58,28 @@ dependencies:
58
58
  requirements:
59
59
  - - "~>"
60
60
  - !ruby/object:Gem::Version
61
- version: 0.88.0
61
+ version: '1.8'
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.88.0
68
+ version: '1.8'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rubocop-rspec
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '2.1'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '2.1'
69
83
  - !ruby/object:Gem::Dependency
70
84
  name: strong_versions
71
85
  requirement: !ruby/object:Gem::Requirement
@@ -84,13 +98,15 @@ description: Allow developers to optionally include a set of development gems wi
84
98
  adding to the Gemfile.
85
99
  email:
86
100
  - git@bob.frl
87
- executables: []
101
+ executables:
102
+ - devpack
88
103
  extensions: []
89
104
  extra_rdoc_files: []
90
105
  files:
91
106
  - ".gitignore"
92
107
  - ".rspec"
93
108
  - ".rubocop.yml"
109
+ - ".ruby-version"
94
110
  - CHANGELOG.md
95
111
  - Gemfile
96
112
  - LICENSE.txt
@@ -100,6 +116,7 @@ files:
100
116
  - bin/console
101
117
  - bin/setup
102
118
  - devpack.gemspec
119
+ - exe/devpack
103
120
  - lib/devpack.rb
104
121
  - lib/devpack/config.rb
105
122
  - lib/devpack/gem_glob.rb
@@ -117,6 +134,7 @@ metadata:
117
134
  homepage_uri: https://github.com/bobf/devpack
118
135
  source_code_uri: https://github.com/bobf/devpack
119
136
  changelog_uri: https://github.com/bobf/devpack/blob/master/CHANGELOG.md
137
+ rubygems_mfa_required: 'true'
120
138
  post_install_message:
121
139
  rdoc_options: []
122
140
  require_paths:
@@ -125,14 +143,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
125
143
  requirements:
126
144
  - - ">="
127
145
  - !ruby/object:Gem::Version
128
- version: 2.3.0
146
+ version: 2.5.0
129
147
  required_rubygems_version: !ruby/object:Gem::Requirement
130
148
  requirements:
131
149
  - - ">="
132
150
  - !ruby/object:Gem::Version
133
151
  version: '0'
134
152
  requirements: []
135
- rubygems_version: 3.1.2
153
+ rubyforge_project:
154
+ rubygems_version: 2.7.6.2
136
155
  signing_key:
137
156
  specification_version: 4
138
157
  summary: Conveniently tailor your development environment