gemfilelint 0.1.0 → 0.2.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: '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