gemfilelint 0.1.0 → 0.2.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: '08f43710d45876141d5a462ef860d5d52f3b782af673be0f8cf5bde39ea269bc'
4
- data.tar.gz: a81951955e4d2a60b4771ebc45f7adbd9a6150f14de2f34f162497ef7fbb2d00
3
+ metadata.gz: ed679ee045c3d39a031c54a7bc7b17a5912920975a46fbf4d8919a99c6216008
4
+ data.tar.gz: cdfd4a5b93dc1c6c2eee7917b11f3000e82f5604223cde31c067b5b433a33729
5
5
  SHA512:
6
- metadata.gz: fb7f7ebdd672f505a5a1dbb91cc36dfc2ddafabfa470101931b05253f51a3e33df82175a5f0c06789d93455aefdce3cff1cbc839178b0a27f2c9c83354442237
7
- data.tar.gz: 6aac9050ccf83d666dc9f640e0f0bc95fbd56ecec061d90f7da8ef2b8ae958406ec41b4e7bcc3de3df787046e68c596750225895fd86f96dc7ae181ca37c6578
6
+ metadata.gz: be1de4b80eee06591d4bd0b85faf0ed50208f5d7f8de04f8dc7a95a339202629ce0bd5b0c81bee750581025bcdbabab1768afc307ac036c8e30c06f04ee2a433
7
+ data.tar.gz: 694bf077b0ef17a588bccb615ddce0ce865d6e1131fe01d20589a7098748d41c4fe170b22b83b532b2f3fce453b7eeefc535330c2468f8398c1f4eca8337924d
data/CHANGELOG.md ADDED
@@ -0,0 +1,22 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
6
+
7
+ ## [Unreleased]
8
+
9
+ ## [0.2.0] - 2020-02-20
10
+
11
+ ### Added
12
+
13
+ - Allow linting multiple Gemfile paths in one command.
14
+
15
+ ## [0.1.0] - 2020-02-20
16
+
17
+ ### Added
18
+
19
+ - 🎉 Initial release.
20
+
21
+ [unreleased]: https://github.com/kddeisz/gemfilelint/compare/v0.1.0...HEAD
22
+ [0.1.0]: https://github.com/kddeisz/gemfilelint/compare/935da5...v0.1.0
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- gemfilelint (0.1.0)
4
+ gemfilelint (0.2.0)
5
5
  bundler
6
6
 
7
7
  GEM
data/exe/gemfilelint CHANGED
@@ -4,4 +4,12 @@
4
4
  $LOAD_PATH.unshift(File.expand_path(File.join('..', 'lib'), __dir__))
5
5
  require 'gemfilelint'
6
6
 
7
- exit Gemfilelint.lint(ARGV[0] || 'Gemfile')
7
+ gemfiles = ARGV.any? ? ARGV : './Gemfile'
8
+
9
+ invalid = gemfiles.reject { |gemfile| File.file?(gemfile) }
10
+ if invalid.any?
11
+ warn("Could not find a gemfile at: #{invalid.join(', ')}")
12
+ exit 2
13
+ end
14
+
15
+ exit Gemfilelint.lint(*gemfiles) ? 0 : 1
data/lib/gemfilelint.rb CHANGED
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'delegate'
3
4
  require 'logger'
4
5
 
5
6
  require 'bundler'
@@ -8,8 +9,23 @@ require 'bundler/similarity_detector'
8
9
  require 'gemfilelint/version'
9
10
 
10
11
  module Gemfilelint
12
+ class SpellChecker
13
+ attr_reader :detector, :haystack
14
+
15
+ def initialize(haystack)
16
+ @detector = Bundler::SimilarityDetector.new(haystack)
17
+ @haystack = haystack
18
+ end
19
+
20
+ def correct(needle)
21
+ return [] if haystack.include?(needle)
22
+
23
+ detector.similar_words(needle)
24
+ end
25
+ end
26
+
11
27
  module Offenses
12
- class Dependency < Struct.new(:name, :suggestions)
28
+ class Dependency < Struct.new(:path, :name, :suggestions)
13
29
  def to_s
14
30
  <<~ERR
15
31
  Gem \"#{name}\" is possibly misspelled, suggestions:
@@ -18,7 +34,13 @@ module Gemfilelint
18
34
  end
19
35
  end
20
36
 
21
- class Remote < Struct.new(:name, :suggestions)
37
+ class InvalidGemfile < Struct.new(:path)
38
+ def to_s
39
+ "Gemfile at \"#{path}\" is invalid."
40
+ end
41
+ end
42
+
43
+ class Remote < Struct.new(:path, :name, :suggestions)
22
44
  def to_s
23
45
  <<~ERR
24
46
  Source \"#{name}\" is possibly misspelled, suggestions:
@@ -28,24 +50,64 @@ module Gemfilelint
28
50
  end
29
51
  end
30
52
 
31
- class Linter
32
- class SpellChecker
33
- attr_reader :detector, :haystack
53
+ module Parser
54
+ class Valid < Struct.new(:path, :dsl)
55
+ def each_offense
56
+ dependencies.each do |dependency|
57
+ yield dependency_offense_for(dependency)
58
+ end
59
+
60
+ remotes.each do |remote|
61
+ yield remote_offense_for(remote)
62
+ end
63
+ end
64
+
65
+ private
66
+
67
+ def dependencies
68
+ dsl.dependencies.map(&:name)
69
+ end
70
+
71
+ def dependency_offense_for(name)
72
+ corrections = Gemfilelint.dependencies.correct(name)
73
+ return if corrections.empty?
74
+
75
+ Offenses::Dependency.new(path, name, corrections.first(5))
76
+ end
34
77
 
35
- def initialize(haystack)
36
- @detector = Bundler::SimilarityDetector.new(haystack)
37
- @haystack = haystack
78
+ # Lol wut, there has got to be a better way to do this
79
+ def remotes
80
+ dsl
81
+ .instance_variable_get(:@sources)
82
+ .instance_variable_get(:@rubygems_aggregate)
83
+ .remotes
84
+ .map(&:to_s)
38
85
  end
39
86
 
40
- def correct(needle)
41
- return [] if haystack.include?(needle)
87
+ def remote_offense_for(uri)
88
+ corrections = Gemfilelint.remotes.correct(uri)
89
+ return if corrections.empty?
90
+
91
+ Offenses::Remote.new(path, uri, corrections)
92
+ end
93
+ end
42
94
 
43
- detector.similar_words(needle)
95
+ class Invalid < Struct.new(:path)
96
+ def each_offense
97
+ yield Offenses::InvalidGemfile.new(path)
44
98
  end
45
99
  end
46
100
 
101
+ def self.for(path)
102
+ Valid.new(path, Bundler::Dsl.new.tap { |dsl| dsl.eval_gemfile(path) })
103
+ rescue Bundler::Dsl::DSLError
104
+ Invalid.new(path)
105
+ end
106
+ end
107
+
108
+ class Linter
47
109
  module ANSIColor
48
- CODES = { green: 32, magenta: 35 }.freeze
110
+ CODES = { green: 32, magenta: 35, cyan: 36 }.freeze
49
111
 
50
112
  refine String do
51
113
  def colorize(code)
@@ -56,23 +118,19 @@ module Gemfilelint
56
118
 
57
119
  using ANSIColor
58
120
 
59
- attr_reader :dependency_checker, :remote_checker, :logger
60
-
61
- def initialize
62
- common_gems = File.read(File.expand_path('gems.txt', __dir__)).split("\n")
121
+ attr_reader :logger
63
122
 
64
- @dependency_checker = SpellChecker.new(common_gems)
65
- @remote_checker = SpellChecker.new(['https://rubygems.org/'])
123
+ def initialize(logger: nil)
124
+ @logger = logger || make_logger
66
125
  end
67
126
 
68
127
  # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
69
- def lint(path, logger: nil)
70
- logger ||= make_logger
128
+ def lint(*paths)
129
+ logger.info("Inspecting gemfiles at #{paths.join(', ')}\n")
71
130
 
72
- logger.info("Inspecting gemfile at #{path}\n")
73
131
  offenses = []
74
132
 
75
- each_offense_for(path) do |offense|
133
+ each_offense_for(paths) do |offense|
76
134
  if offense
77
135
  offenses << offense
78
136
  logger.info('W'.colorize(:magenta))
@@ -82,54 +140,53 @@ module Gemfilelint
82
140
  end
83
141
 
84
142
  logger.info("\n")
85
- return 0 if offenses.empty?
86
143
 
87
- prefix = 'W'.colorize(:magenta)
88
- messages = offenses.map { |offense| "#{prefix}: #{offense}\n" }
89
- logger.info("\nOffenses:\n\n#{messages.join}\n")
90
-
91
- 1
144
+ if offenses.empty?
145
+ true
146
+ else
147
+ messages = offenses.map { |offense| offense_to_message(offense) }
148
+ logger.info("\nOffenses:\n\n#{messages.join("\n")}\n")
149
+ false
150
+ end
92
151
  end
93
152
  # rubocop:enable Metrics/AbcSize, Metrics/MethodLength
94
153
 
95
154
  private
96
155
 
97
- def make_logger
98
- Logger.new(STDOUT).tap do |logger|
99
- logger.level = :info
100
- logger.formatter = ->(*, message) { message }
156
+ def each_offense_for(paths)
157
+ paths.each do |path|
158
+ Parser.for(path).each_offense do |offense|
159
+ yield offense
160
+ end
101
161
  end
102
162
  end
103
163
 
104
- def each_offense_for(path)
105
- dsl = Bundler::Dsl.new
106
- dsl.eval_gemfile(path)
107
-
108
- # Lol wut, there has got to be a better way to do this
109
- source_list = dsl.instance_variable_get(:@sources)
110
- rubygems = source_list.instance_variable_get(:@rubygems_aggregate)
111
-
112
- dsl.dependencies.each do |dependency|
113
- yield dependency_offense_for(dependency.name)
164
+ def make_logger
165
+ Logger.new(STDOUT).tap do |creating|
166
+ creating.level = :info
167
+ creating.formatter = ->(*, message) { message }
114
168
  end
169
+ end
115
170
 
116
- rubygems.remotes.each do |remote|
117
- yield remote_offense_for(remote.to_s)
118
- end
171
+ def offense_to_message(offense)
172
+ "#{offense.path.colorize(:cyan)}: #{'W'.colorize(:magenta)}: #{offense}"
119
173
  end
174
+ end
120
175
 
121
- def dependency_offense_for(name)
122
- corrections = dependency_checker.correct(name)
123
- Offenses::Dependency.new(name, corrections.first(5)) if corrections.any?
176
+ class << self
177
+ def dependencies
178
+ @dependencies ||=
179
+ SpellChecker.new(
180
+ File.read(File.expand_path('gems.txt', __dir__)).split("\n")
181
+ )
124
182
  end
125
183
 
126
- def remote_offense_for(uri)
127
- corrections = remote_checker.correct(uri)
128
- Offenses::Remote.new(uri, corrections) if corrections.any?
184
+ def remotes
185
+ @remotes ||= SpellChecker.new(['https://rubygems.org/'])
129
186
  end
130
- end
131
187
 
132
- def self.lint(path, logger: nil)
133
- Linter.new.lint(path, logger: logger)
188
+ def lint(*paths, logger: nil)
189
+ Linter.new(logger: logger).lint(*paths)
190
+ end
134
191
  end
135
192
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Gemfilelint
4
- VERSION = '0.1.0'
4
+ VERSION = '0.2.0'
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: gemfilelint
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kevin Deisz
@@ -78,6 +78,7 @@ files:
78
78
  - ".gitignore"
79
79
  - ".mergify.yml"
80
80
  - ".rubocop.yml"
81
+ - CHANGELOG.md
81
82
  - CODE_OF_CONDUCT.md
82
83
  - Gemfile
83
84
  - Gemfile.lock