devpack 0.3.0 → 0.4.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: 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