gem_lint 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG ADDED
@@ -0,0 +1,2 @@
1
+ v0.0.1 (29th November 2010)
2
+ * initial release
data/README.rdoc ADDED
@@ -0,0 +1,56 @@
1
+ = GemLint
2
+
3
+ Sanity check your rubygems for common issues. Missing readmes, bundled pkg
4
+ directories, encoding issues in the gemspec, etc.
5
+
6
+ Each test is classified as an error or warning. Errors are issues that (by my
7
+ reading) violate the documented gem specs. Warnings are either best practices
8
+ recommended by members of the community or arbitrary things that annoy me
9
+ personally.
10
+
11
+ == Usage
12
+
13
+ gem install gem-lint
14
+ gem lint somefile-0.0.1.gem
15
+
16
+ === API
17
+
18
+ To check a gem programmatically:
19
+
20
+ require "gem_lint"
21
+
22
+ runner = GemLint::Runner.new("widgets-1.0.gem")
23
+ puts runner.name
24
+ puts runner.version
25
+ puts runner.email
26
+ puts runner.tags
27
+
28
+ == Status
29
+
30
+ This a spike. It has few tests and is a proof of concept. Most of it was written at
31
+ a Railscamp and therefore is probably of suspect quality.
32
+
33
+ == Compatability
34
+
35
+ Rubygems 1.3.2 or greater is required, otherwise it's pure ruby should run on most
36
+ ruby VMs.
37
+
38
+ == Developing
39
+
40
+ To add new checks, create a new strategy in lib/gem_lint/strategies with a matching
41
+ spec in spec/strategies.
42
+
43
+ View other strategies for an idea of the required contract. There are existing
44
+ specs to ensure all strategies confirm to the contract, so keep the full spec
45
+ suit (using "rake") green.
46
+
47
+ Full source is on github at https://github.com/yob/gem-lint
48
+
49
+ == Ideas
50
+
51
+ * command for users to display extra info on a tag a gem failed
52
+
53
+ == Further Reading
54
+
55
+ * http://chneukirchen.github.com/rps/
56
+ * http://blog.segment7.net/articles/2010/11/15/how-to-name-gems
@@ -0,0 +1,123 @@
1
+ # coding: utf-8
2
+
3
+ module GemLint
4
+ class Runner
5
+
6
+ attr_reader :tags, :tags_with_level, :email, :name, :version
7
+
8
+ def initialize(filename)
9
+ raise ArgumentError, "'#{filename}' does not exist" unless File.file?(filename.to_s)
10
+
11
+ @filename = filename
12
+ init_vars
13
+ end
14
+
15
+ private
16
+
17
+ def init_vars
18
+ unpack_gem
19
+ @tags = collect_tags
20
+ @tags_with_level = collect_tags_with_level
21
+ @email = spec ? spec.email : nil
22
+ @name = spec ? spec.name : nil
23
+ @version = spec ? spec.version.to_s : nil
24
+ cleanup
25
+ end
26
+
27
+ # returns an array of symbols, each one indicating a test the provided gem
28
+ # failed. Unpacks the gem to a temporary location and cleans up after
29
+ # itself.
30
+ #
31
+ def collect_tags
32
+ if unpack_successful?
33
+ failed_strategies.map { |s|
34
+ s.tag
35
+ }
36
+ else
37
+ ["unpack-failed"]
38
+ end
39
+ end
40
+
41
+ def collect_tags_with_level
42
+ if unpack_successful?
43
+ failed_strategies.map { |s|
44
+ "#{s.level_char}: #{s.tag}"
45
+ }.sort
46
+ else
47
+ ["E: unpack-failed"]
48
+ end
49
+ end
50
+
51
+ def failed_strategies
52
+ if unpack_successful?
53
+ @failed_strategies ||= GemLint.strategies.map { |s|
54
+ s.new(visitor_args)
55
+ }.select { |s|
56
+ s.fail?
57
+ }
58
+ else
59
+ @failed_strategies ||= []
60
+ end
61
+ end
62
+
63
+ def visitor_args
64
+ @visitor_args ||= {
65
+ :filename => @filename,
66
+ :data_path => data_path,
67
+ :metadata_path => metadata_file
68
+ }
69
+ end
70
+
71
+ def unpack_successful?
72
+ File.directory?(data_path) && File.file?(metadata_file)
73
+ end
74
+
75
+ def data_path
76
+ @data_path ||= File.join(unpack_path, "data")
77
+ end
78
+
79
+ def data_file
80
+ @data_file ||= File.join(unpack_path, "data.tar.gz")
81
+ end
82
+
83
+ def metadata_gz_file
84
+ @metadata_gz_file ||= File.join(unpack_path, "metadata.gz")
85
+ end
86
+
87
+ def metadata_file
88
+ @metadata_file ||= File.join(unpack_path, "metadata")
89
+ end
90
+
91
+ def spec
92
+ if @spec || File.file?(metadata_file)
93
+ @spec ||= YAML.load_file(metadata_file)
94
+ else
95
+ nil
96
+ end
97
+ end
98
+
99
+ def unpack_path
100
+ @unpack_path ||= File.join(Dir.tmpdir, rand(100000).to_s)
101
+ end
102
+
103
+ # TODO: update this to use native ruby untar
104
+ def unpack_gem
105
+ Dir.mkdir(unpack_path)
106
+ Dir.mkdir(data_path)
107
+
108
+ `tar -xkC #{unpack_path} -f #{@filename} > /dev/null 2>&1`
109
+ `tar -xzkC #{data_path} -f #{data_file} > /dev/null 2>&1`
110
+ `gunzip #{metadata_file} > /dev/null 2>&1`
111
+
112
+ FileUtils.remove_entry_secure(data_file) if File.file?(data_file)
113
+
114
+ true
115
+ end
116
+
117
+ def cleanup
118
+ return unless File.directory?(unpack_path)
119
+
120
+ FileUtils.remove_entry_secure(unpack_path)
121
+ end
122
+ end
123
+ end
@@ -0,0 +1,40 @@
1
+ # coding: utf-8
2
+
3
+ module GemLint
4
+ module Strategies
5
+ class AbstractStrategy
6
+ attr_reader :filename, :path, :metadata_path
7
+
8
+ def initialize(args = {})
9
+ @filename = args[:filename]
10
+ @path = args[:data_path]
11
+ @metadata_path = args[:metadata_path]
12
+
13
+ raise ArgumentError, "#{@path} is not a directory" unless File.directory?(@path)
14
+ raise ArgumentError, "#{@metadata_path} is not a file" unless File.file?(@metadata_path)
15
+ raise ArgumentError, ":filename must be a filename ending in gem" unless @filename.to_s[-4,4] == ".gem"
16
+ end
17
+
18
+ def level_char
19
+ self.level.to_s[0,1].upcase
20
+ end
21
+
22
+ def description
23
+ raise "this should be implemented by concrete subclasses"
24
+ end
25
+
26
+ def fail?
27
+ raise "this should be implemented by concrete subclasses"
28
+ end
29
+
30
+ def level
31
+ raise "this should be implemented by concrete subclasses"
32
+ end
33
+
34
+ def tag
35
+ raise "this should be implemented by concrete subclasses"
36
+ end
37
+
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,39 @@
1
+ module GemLint
2
+ module Strategies
3
+ class BinEndsWithRbStrategy < AbstractStrategy
4
+
5
+ def description
6
+ "A file in bin/ ends in .rb"
7
+ end
8
+
9
+ def tag
10
+ :"bin-ends-with-rb"
11
+ end
12
+
13
+ def level
14
+ :warning
15
+ end
16
+
17
+ def fail?
18
+ bin_files.any? { |path| path[-3,3] == ".rb" }
19
+ end
20
+
21
+ private
22
+
23
+ def bin_files
24
+ all_files.select { |path|
25
+ path[0,4] == "bin/" && File.file?(self.path + "/" + path)
26
+ }
27
+ end
28
+
29
+ def all_files
30
+ paths = []
31
+ Find.find(self.path) { |path| paths << path }
32
+ paths.map { |path|
33
+ path.gsub(self.path + "/","")
34
+ }
35
+ end
36
+
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,43 @@
1
+ module GemLint
2
+ module Strategies
3
+ class BinWithoutShebangStrategy < AbstractStrategy
4
+
5
+ def description
6
+ "A file in bin/ doesn't have a shebang on the first line"
7
+ end
8
+
9
+ def tag
10
+ :"bin-without-shebang"
11
+ end
12
+
13
+ def level
14
+ :warning
15
+ end
16
+
17
+ def fail?
18
+ bin_files.any? { |path|
19
+ data = File.open(self.path + "/" + path) { |f| f.read }
20
+ first_line = data.split("\n")[0]
21
+ !first_line.to_s.include?("env ruby")
22
+ }
23
+ end
24
+
25
+ private
26
+
27
+ def bin_files
28
+ all_files.select { |path|
29
+ path[0,4] == "bin/" && File.file?(self.path + "/" + path)
30
+ }
31
+ end
32
+
33
+ def all_files
34
+ paths = []
35
+ Find.find(self.path) { |path| paths << path }
36
+ paths.map { |path|
37
+ path.gsub(self.path + "/","")
38
+ }
39
+ end
40
+
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,32 @@
1
+ require 'find'
2
+
3
+ module GemLint
4
+ module Strategies
5
+ class CapitalsInNameStrategy < AbstractStrategy
6
+
7
+ def description
8
+ "It's strongly recommended that your gem name be all lower case"
9
+ end
10
+
11
+ def tag
12
+ :"capitals-in-name"
13
+ end
14
+
15
+ def level
16
+ :warning
17
+ end
18
+
19
+ def fail?
20
+ spec.name.match(/[A-Z]/)
21
+ end
22
+
23
+ private
24
+
25
+ def spec
26
+ @spec ||= YAML.load(File.read(@metadata_path))
27
+ end
28
+
29
+ end
30
+ end
31
+ end
32
+
@@ -0,0 +1,32 @@
1
+ module GemLint
2
+ module Strategies
3
+ class ChangelogStrategy < AbstractStrategy
4
+
5
+ def description
6
+ "Gem contains no changelog or history file"
7
+ end
8
+
9
+ def tag
10
+ :"no-changelog-or-history"
11
+ end
12
+
13
+ def level
14
+ :warning
15
+ end
16
+
17
+ def fail?
18
+ root_files.none? do |filename|
19
+ dfile = filename.downcase
20
+ dfile.include?("changelog") || dfile.include?("history")
21
+ end
22
+ end
23
+
24
+ private
25
+
26
+ def root_files
27
+ Dir.entries(self.path)
28
+ end
29
+
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,31 @@
1
+ module GemLint
2
+ module Strategies
3
+ class ContainsGemFileStrategy < AbstractStrategy
4
+
5
+ def description
6
+ "Gem contains at least 1 other gem file"
7
+ end
8
+
9
+ def tag
10
+ :"contains-gem-file"
11
+ end
12
+
13
+ def level
14
+ :warning
15
+ end
16
+
17
+ def fail?
18
+ spec.files.any? { |filename|
19
+ filename.match(/\A.*\.gem\Z/)
20
+ }
21
+ end
22
+
23
+ private
24
+
25
+ def spec
26
+ @spec ||= YAML.load(File.read(@metadata_path))
27
+ end
28
+
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,30 @@
1
+ module GemLint
2
+ module Strategies
3
+ class CsvAuthorsStrategy < AbstractStrategy
4
+
5
+ def description
6
+ "Authors field in spec uses a comma or semicolon to specify multiple authors instead of an array"
7
+ end
8
+
9
+ def tag
10
+ :"csv-authors"
11
+ end
12
+
13
+ def level
14
+ :error
15
+ end
16
+
17
+ def fail?
18
+ yaml.authors.is_a?(Array) &&
19
+ (yaml.authors.first.include?(",") || yaml.authors.first.include?(";"))
20
+ end
21
+
22
+ private
23
+
24
+ def yaml
25
+ @yaml ||= YAML.load(File.read(@metadata_path))
26
+ end
27
+
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,30 @@
1
+ module GemLint
2
+ module Strategies
3
+ class CsvEmailStrategy < AbstractStrategy
4
+
5
+ def description
6
+ "Email field in spec uses CSV to specify multiple emails instead of an array"
7
+ end
8
+
9
+ def tag
10
+ :"csv-email"
11
+ end
12
+
13
+ def level
14
+ :error
15
+ end
16
+
17
+ def fail?
18
+ (yaml.email.is_a?(String) && (yaml.email.include?(",") || yaml.email.include?(";"))) ||
19
+ (yaml.email.is_a?(Array) && (yaml.email.first.include?(",") || yaml.email.first.include?(";")))
20
+ end
21
+
22
+ private
23
+
24
+ def yaml
25
+ @yaml ||= YAML.load(File.read(@metadata_path))
26
+ end
27
+
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,29 @@
1
+ module GemLint
2
+ module Strategies
3
+ class DuplicateAuthorsStrategy < AbstractStrategy
4
+
5
+ def description
6
+ "Authors field in spec should have no duplicate values"
7
+ end
8
+
9
+ def tag
10
+ :"duplicate-authors"
11
+ end
12
+
13
+ def level
14
+ :warning
15
+ end
16
+
17
+ def fail?
18
+ yaml.authors.is_a?(Array) && yaml.authors.uniq.size != yaml.authors.size
19
+ end
20
+
21
+ private
22
+
23
+ def yaml
24
+ @yaml ||= YAML.load(File.read(@metadata_path))
25
+ end
26
+
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,29 @@
1
+ module GemLint
2
+ module Strategies
3
+ class DuplicateEmailStrategy < AbstractStrategy
4
+
5
+ def description
6
+ "Email field in spec should have no duplicate values"
7
+ end
8
+
9
+ def tag
10
+ :"csv-email"
11
+ end
12
+
13
+ def level
14
+ :warning
15
+ end
16
+
17
+ def fail?
18
+ yaml.email.is_a?(Array) && yaml.email.uniq.size != yaml.email.size
19
+ end
20
+
21
+ private
22
+
23
+ def yaml
24
+ @yaml ||= YAML.load(File.read(@metadata_path))
25
+ end
26
+
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,31 @@
1
+ module GemLint
2
+ module Strategies
3
+ class PkgDirStrategy < AbstractStrategy
4
+
5
+ def description
6
+ "Gem contains a directory in the root called pkg"
7
+ end
8
+
9
+ def tag
10
+ :"pkg-dir"
11
+ end
12
+
13
+ def level
14
+ :warning
15
+ end
16
+
17
+ def fail?
18
+ root_files.any? { |filename|
19
+ filename.match(/pkg.*/)
20
+ }
21
+ end
22
+
23
+ private
24
+
25
+ def root_files
26
+ Dir.entries(self.path)
27
+ end
28
+
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,31 @@
1
+ module GemLint
2
+ module Strategies
3
+ class ReadmeStrategy < AbstractStrategy
4
+
5
+ def description
6
+ "Gem contains no readme file"
7
+ end
8
+
9
+ def tag
10
+ :"no-readme"
11
+ end
12
+
13
+ def level
14
+ :warning
15
+ end
16
+
17
+ def fail?
18
+ root_files.none? do |filename|
19
+ filename.downcase.include?("readme")
20
+ end
21
+ end
22
+
23
+ private
24
+
25
+ def root_files
26
+ Dir.entries(self.path)
27
+ end
28
+
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,42 @@
1
+ require 'find'
2
+
3
+ module GemLint
4
+ module Strategies
5
+ class RequireMatchesGemnameStrategy < AbstractStrategy
6
+
7
+ def description
8
+ "Gem cannot be loaded by require '#{preferred_basename}'"
9
+ end
10
+
11
+ def tag
12
+ :"require-doesnt-match-gemname"
13
+ end
14
+
15
+ def level
16
+ :warning
17
+ end
18
+
19
+ # TODO: does thi need to be beefed up to recognise extensions
20
+ #
21
+ def fail?
22
+ !spec.files.include?(preferred_filename)
23
+ end
24
+
25
+ private
26
+
27
+ def preferred_basename
28
+ "lib/" + spec.name.tr("-","/")
29
+ end
30
+
31
+ def preferred_filename
32
+ preferred_basename + ".rb"
33
+ end
34
+
35
+ def spec
36
+ @spec ||= YAML.load(File.read(@metadata_path))
37
+ end
38
+
39
+ end
40
+ end
41
+ end
42
+
@@ -0,0 +1,38 @@
1
+ require 'find'
2
+
3
+ module GemLint
4
+ module Strategies
5
+ class RubyFileLocationStrategy < AbstractStrategy
6
+
7
+ def description
8
+ "Gem contains ruby files outside of lib, test and spec"
9
+ end
10
+
11
+ def tag
12
+ :"ruby-files-outside-lib-test-spec"
13
+ end
14
+
15
+ def level
16
+ :warning
17
+ end
18
+
19
+ def fail?
20
+ all_files.any? do |filename|
21
+ filename[0,3] != "lib" && filename[0,4] != "spec" &&
22
+ filename[0,4] != "test" && filename[-3,3] == ".rb"
23
+ end
24
+ end
25
+
26
+ private
27
+
28
+ def all_files
29
+ paths = []
30
+ Find.find(self.path) { |path| paths << path }
31
+ paths.map { |path|
32
+ path.gsub(self.path + "/","")
33
+ }
34
+ end
35
+
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,29 @@
1
+ module GemLint
2
+ module Strategies
3
+ class StringEmailStrategy < AbstractStrategy
4
+
5
+ def description
6
+ "Email field in spec is a String instead of an Array"
7
+ end
8
+
9
+ def tag
10
+ :"string-email"
11
+ end
12
+
13
+ def level
14
+ :error
15
+ end
16
+
17
+ def fail?
18
+ yaml.email.is_a?(String)
19
+ end
20
+
21
+ private
22
+
23
+ def yaml
24
+ @yaml ||= YAML.load(File.read(@metadata_path))
25
+ end
26
+
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,37 @@
1
+ require 'find'
2
+
3
+ module GemLint
4
+ module Strategies
5
+ class TestFilesInFilesAttributeStrategy < AbstractStrategy
6
+
7
+ def description
8
+ "Gem metadata includes test files in files attribute. Use test_files instead"
9
+ end
10
+
11
+ def tag
12
+ :"test-files-in-files-attribute"
13
+ end
14
+
15
+ def level
16
+ :warning
17
+ end
18
+
19
+ def fail?
20
+ orig_files.any? { |filename|
21
+ filename[-8,8] == "_spec.rb" || filename[-8,8] == "_test.rb"
22
+ }
23
+ end
24
+
25
+ private
26
+
27
+ def orig_files
28
+ @orig_files ||= yaml.files - yaml.test_files
29
+ end
30
+
31
+ def yaml
32
+ @yaml ||= YAML.load(File.read(@metadata_path))
33
+ end
34
+
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,26 @@
1
+ module GemLint
2
+ module Strategies
3
+ class Utf8MetadataStrategy < AbstractStrategy
4
+
5
+ def description
6
+ "Ensure the gem metadata is all utf8 encoded"
7
+ end
8
+
9
+ def tag
10
+ :"utf8-metadata"
11
+ end
12
+
13
+ def level
14
+ :error
15
+ end
16
+
17
+ def fail?
18
+ File.read(@metadata_path).unpack("U*")
19
+ false
20
+ rescue
21
+ true
22
+ end
23
+
24
+ end
25
+ end
26
+ end
data/lib/gem_lint.rb ADDED
@@ -0,0 +1,25 @@
1
+ # coding: utf-8
2
+
3
+ # stdlib
4
+ require 'tmpdir'
5
+ require 'fileutils'
6
+
7
+ # our own code
8
+ require 'gem_lint/runner'
9
+
10
+ # dynamically load all available strategies
11
+ strategies_path = File.dirname(__FILE__) + "/gem_lint/strategies/**/*.rb"
12
+ Dir[strategies_path].sort.each {|f| require f }
13
+
14
+ module GemLint
15
+
16
+ # return an array of all strategy classes
17
+ #
18
+ def self.strategies
19
+ GemLint::Strategies.constants.sort.select { |class_name|
20
+ class_name != "AbstractStrategy"
21
+ }.map { |class_name|
22
+ GemLint::Strategies.const_get(class_name)
23
+ }
24
+ end
25
+ end
@@ -0,0 +1,35 @@
1
+ require 'rubygems/command'
2
+ require 'gem_lint'
3
+
4
+ class Gem::Commands::LintCommand < Gem::Command
5
+ def description # :nodoc:
6
+ 'Check a gem file for common mistakes and errors'
7
+ end
8
+
9
+ def arguments # :nodoc:
10
+ "GEM built gem to check"
11
+ end
12
+
13
+ def usage # :nodoc:
14
+ "#{program_name} GEM"
15
+ end
16
+
17
+ def initialize
18
+ super 'lint', description
19
+ end
20
+
21
+ def execute
22
+ runner = GemLint::Runner.new(get_one_gem_name)
23
+ if runner.tags.empty?
24
+ puts "No test failures!"
25
+ puts
26
+ else
27
+ runner.tags_with_level.each do |tag|
28
+ puts "- #{tag}"
29
+ end
30
+ puts
31
+ exit 1
32
+ end
33
+ end
34
+
35
+ end
@@ -0,0 +1,3 @@
1
+ require 'rubygems/command_manager'
2
+
3
+ Gem::CommandManager.instance.register_command :lint
metadata ADDED
@@ -0,0 +1,150 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: gem_lint
3
+ version: !ruby/object:Gem::Version
4
+ hash: 29
5
+ prerelease: false
6
+ segments:
7
+ - 0
8
+ - 0
9
+ - 1
10
+ version: 0.0.1
11
+ platform: ruby
12
+ authors:
13
+ - James Healy
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2010-11-29 00:00:00 -08:00
19
+ default_executable:
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ name: rake
23
+ prerelease: false
24
+ requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ hash: 3
30
+ segments:
31
+ - 0
32
+ version: "0"
33
+ type: :development
34
+ version_requirements: *id001
35
+ - !ruby/object:Gem::Dependency
36
+ name: rcov
37
+ prerelease: false
38
+ requirement: &id002 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ hash: 3
44
+ segments:
45
+ - 0
46
+ version: "0"
47
+ type: :development
48
+ version_requirements: *id002
49
+ - !ruby/object:Gem::Dependency
50
+ name: roodi
51
+ prerelease: false
52
+ requirement: &id003 !ruby/object:Gem::Requirement
53
+ none: false
54
+ requirements:
55
+ - - ">="
56
+ - !ruby/object:Gem::Version
57
+ hash: 3
58
+ segments:
59
+ - 0
60
+ version: "0"
61
+ type: :development
62
+ version_requirements: *id003
63
+ - !ruby/object:Gem::Dependency
64
+ name: rspec
65
+ prerelease: false
66
+ requirement: &id004 !ruby/object:Gem::Requirement
67
+ none: false
68
+ requirements:
69
+ - - ~>
70
+ - !ruby/object:Gem::Version
71
+ hash: 3
72
+ segments:
73
+ - 2
74
+ - 0
75
+ version: "2.0"
76
+ type: :development
77
+ version_requirements: *id004
78
+ description: Adds a 'lint' command rubygems that will pront a list of possible errors in a gem
79
+ email:
80
+ - james@yob.id.au
81
+ executables: []
82
+
83
+ extensions: []
84
+
85
+ extra_rdoc_files: []
86
+
87
+ files:
88
+ - lib/gem_lint.rb
89
+ - lib/rubygems_plugin.rb
90
+ - lib/rubygems/commands/lint_command.rb
91
+ - lib/gem_lint/runner.rb
92
+ - lib/gem_lint/strategies/require_matches_gemname_strategy.rb
93
+ - lib/gem_lint/strategies/utf8_metadata_strategy.rb
94
+ - lib/gem_lint/strategies/readme_strategy.rb
95
+ - lib/gem_lint/strategies/changelog_strategy.rb
96
+ - lib/gem_lint/strategies/duplicate_authors_strategy.rb
97
+ - lib/gem_lint/strategies/bin_ends_with_rb_strategy.rb
98
+ - lib/gem_lint/strategies/bin_without_shebang_strategy.rb
99
+ - lib/gem_lint/strategies/csv_email_strategy.rb
100
+ - lib/gem_lint/strategies/abstract_strategy.rb
101
+ - lib/gem_lint/strategies/pkg_dir_strategy.rb
102
+ - lib/gem_lint/strategies/capitals_in_name_strategy.rb
103
+ - lib/gem_lint/strategies/test_files_in_files_attribute_strategy.rb
104
+ - lib/gem_lint/strategies/string_email_strategy.rb
105
+ - lib/gem_lint/strategies/contains_gem_file_strategy.rb
106
+ - lib/gem_lint/strategies/ruby_file_location_strategy.rb
107
+ - lib/gem_lint/strategies/duplicate_email_strategy.rb
108
+ - lib/gem_lint/strategies/csv_authors_strategy.rb
109
+ - README.rdoc
110
+ - CHANGELOG
111
+ has_rdoc: true
112
+ homepage: http://github.com/yob/gem-lint
113
+ licenses: []
114
+
115
+ post_install_message:
116
+ rdoc_options:
117
+ - --title
118
+ - Gem Lint
119
+ - --line-numbers
120
+ require_paths:
121
+ - lib
122
+ required_ruby_version: !ruby/object:Gem::Requirement
123
+ none: false
124
+ requirements:
125
+ - - ">="
126
+ - !ruby/object:Gem::Version
127
+ hash: 3
128
+ segments:
129
+ - 0
130
+ version: "0"
131
+ required_rubygems_version: !ruby/object:Gem::Requirement
132
+ none: false
133
+ requirements:
134
+ - - ">="
135
+ - !ruby/object:Gem::Version
136
+ hash: 31
137
+ segments:
138
+ - 1
139
+ - 3
140
+ - 2
141
+ version: 1.3.2
142
+ requirements: []
143
+
144
+ rubyforge_project:
145
+ rubygems_version: 1.3.7
146
+ signing_key:
147
+ specification_version: 3
148
+ summary: Check rubygem files for common mistakes and errors
149
+ test_files: []
150
+