annotate_gem 0.0.8
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 +7 -0
- data/.gitignore +15 -0
- data/Gemfile +7 -0
- data/LICENSE.txt +22 -0
- data/README.md +60 -0
- data/Rakefile +8 -0
- data/annotate_gem.gemspec +28 -0
- data/bin/annotate_gem +8 -0
- data/lib/annotate_gem.rb +8 -0
- data/lib/annotate_gem/cli.rb +41 -0
- data/lib/annotate_gem/gem_line.rb +65 -0
- data/lib/annotate_gem/gemfile.rb +46 -0
- data/lib/annotate_gem/options.rb +66 -0
- data/lib/annotate_gem/spec_finder.rb +43 -0
- data/lib/annotate_gem/version.rb +4 -0
- data/test/annotate_gem/cli_test.rb +23 -0
- data/test/annotate_gem/gem_line_test.rb +79 -0
- data/test/annotate_gem/gemfile_test.rb +71 -0
- data/test/annotate_gem/spec_finder_test.rb +79 -0
- data/test/fixtures/Gemfile +31 -0
- data/test/fixtures/Gemfile_annotated +43 -0
- data/test/test_helper.rb +31 -0
- metadata +130 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 7e0ee9922534ca6c7e763fd617f8e81a461ff4e1
|
4
|
+
data.tar.gz: d60f74fff3ca5131bd5beacbc7729435a973ab01
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 3323eb1ba24e40ca46fb3f75c4ccb72ead72c4c9279fcebe841ee24b7de4eb51af7aa0fd3dd162d2367da6f6e3ea74aabb5c2748bf41cecff5e8d3a92b2c166c
|
7
|
+
data.tar.gz: cbb7b96c3789ff8123299bc315742c727521b1f9eee40abf53c9f3078a4b1033b3ca19e9ae16e9820093cb8fc901c3694448bea26207293df302dc830c9a40ad
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2016 Ivan Tse
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,60 @@
|
|
1
|
+
# AnnotateGem
|
2
|
+
|
3
|
+
`annotate_gem` is command line tool that will add useful comments to your Gemfile. For each gem, `annotate_gem` will create a comment with the gem's description and the gem's website. For example, a Gemfile containing the following
|
4
|
+
|
5
|
+
```ruby
|
6
|
+
gem "rails"
|
7
|
+
gem "nokogiri"
|
8
|
+
gem "brakeman"
|
9
|
+
```
|
10
|
+
|
11
|
+
will be converted into something that is more descriptive and is self-documenting:
|
12
|
+
|
13
|
+
```ruby
|
14
|
+
# Full-stack web application framework. (http://www.rubyonrails.org)
|
15
|
+
gem "rails"
|
16
|
+
# Nokogiri (鋸) is an HTML, XML, SAX, and Reader parser (http://nokogiri.org)
|
17
|
+
gem "nokogiri"
|
18
|
+
# Security vulnerability scanner for Ruby on Rails. (http://brakemanscanner.org)
|
19
|
+
gem "brakeman"
|
20
|
+
```
|
21
|
+
|
22
|
+
The motivation for `annotate_gem` is that developers often open a Gemfile and not know what many of the listed gems are actually for. It's hard to track down which gem is providing which functionality. This is a common problem since many gem names do not reflect the actual feature.
|
23
|
+
|
24
|
+
If you do _not_ want to install the gem, you can also visit <https://annotate_gem.herokuapp.com/> which is a web interface for `annotate_gem`.
|
25
|
+
|
26
|
+
## Installation
|
27
|
+
|
28
|
+
```
|
29
|
+
$ gem install annotate_gem
|
30
|
+
```
|
31
|
+
|
32
|
+
## Usage
|
33
|
+
|
34
|
+
Running `annotate_gem` itself will add comments to the current directory's `Gemfile`.
|
35
|
+
|
36
|
+
```
|
37
|
+
$ cat Gemfile
|
38
|
+
source 'https://rubygems.org'
|
39
|
+
gem "pry"
|
40
|
+
$ annotate_gem
|
41
|
+
$ cat Gemfile
|
42
|
+
source 'https://rubygems.org'
|
43
|
+
# An IRB alternative and runtime developer console (http://pryrepl.org)
|
44
|
+
gem "pry"
|
45
|
+
```
|
46
|
+
|
47
|
+
`annotate_gem` has several options and you can see them via `annotate_gem -h`. `annotate_gem` also works with specifying a single gem name:
|
48
|
+
|
49
|
+
```
|
50
|
+
$ annotate_gem aasm
|
51
|
+
State machine mixin for Ruby objects (https://github.com/aasm/aasm)
|
52
|
+
```
|
53
|
+
|
54
|
+
## Contributing
|
55
|
+
|
56
|
+
1. Fork it ( https://github.com/ivantsepp/annotate_gem/fork )
|
57
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
58
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
59
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
60
|
+
5. Create a new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'annotate_gem/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "annotate_gem"
|
8
|
+
spec.version = AnnotateGem::VERSION
|
9
|
+
spec.authors = ["Ivan Tse"]
|
10
|
+
spec.email = ["ivan.tse1@gmail.com"]
|
11
|
+
spec.summary = AnnotateGem::DESCRIPTION
|
12
|
+
spec.description = <<-DESCRIPTION.gsub(/^\s+/, "").gsub(/\n/, " ")
|
13
|
+
Clarify your dependencies by adding a detailed comment to each line in Gemfile specifying the
|
14
|
+
gem's summary and its website if any.
|
15
|
+
DESCRIPTION
|
16
|
+
spec.homepage = "https://github.com/ivantsepp/annotate_gem"
|
17
|
+
spec.license = "MIT"
|
18
|
+
|
19
|
+
spec.files = `git ls-files -z`.split("\x0")
|
20
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
21
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
22
|
+
spec.require_paths = ["lib"]
|
23
|
+
|
24
|
+
spec.add_runtime_dependency "bundler", "~> 1.11.2"
|
25
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
26
|
+
spec.add_development_dependency "minitest", "~> 5.0"
|
27
|
+
spec.add_development_dependency "mocha", "~> 1.1"
|
28
|
+
end
|
data/bin/annotate_gem
ADDED
data/lib/annotate_gem.rb
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
require "annotate_gem/options"
|
2
|
+
|
3
|
+
module AnnotateGem
|
4
|
+
class CLI
|
5
|
+
def run(args)
|
6
|
+
options = Options.new.parse!(args)
|
7
|
+
if args.empty?
|
8
|
+
run_for_gemfile(options)
|
9
|
+
else
|
10
|
+
run_for_gem(args.pop, options)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def run_for_gemfile(options = {})
|
15
|
+
Bundler.configure
|
16
|
+
gemfile = Gemfile.new(Bundler.default_gemfile, options)
|
17
|
+
gemfile.parse
|
18
|
+
unless gemfile.gem_lines.empty?
|
19
|
+
SpecFinder.find_specs_for(gemfile.gem_lines, &self.method(:print_progress))
|
20
|
+
gemfile.write_comments
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def run_for_gem(gem_name, options = {})
|
25
|
+
gem_line = GemLine.new(name: gem_name, options: options)
|
26
|
+
SpecFinder.find_specs_for(gem_line, &self.method(:print_progress))
|
27
|
+
|
28
|
+
info = gem_line.info
|
29
|
+
info = "No information to show" if info.strip.empty?
|
30
|
+
puts info
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def print_progress(processing, total)
|
36
|
+
print "Fetching gem metadata..." if processing.zero?
|
37
|
+
print "."
|
38
|
+
print "\n" if processing == total
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
module AnnotateGem
|
2
|
+
class GemLine
|
3
|
+
|
4
|
+
attr_accessor :name, :original_line, :location, :prev_line_comment, :spec, :options
|
5
|
+
|
6
|
+
def initialize(*args)
|
7
|
+
named_params = args.last.respond_to?(:[]) && args.last
|
8
|
+
@name = (named_params && named_params[:name]) || args[0]
|
9
|
+
@original_line = (named_params && named_params[:original_line]) || args[1]
|
10
|
+
@location = (named_params && named_params[:location]) || args[2]
|
11
|
+
@prev_line_comment = (named_params && named_params[:prev_line_comment]) || args[3]
|
12
|
+
@options = (named_params && named_params[:options]) || named_params
|
13
|
+
end
|
14
|
+
|
15
|
+
def comment
|
16
|
+
leading_spaces = original_line[0..leading_spaces_count - 1] if leading_spaces_count > 0
|
17
|
+
comment = "#{leading_spaces}# #{info}"
|
18
|
+
end
|
19
|
+
|
20
|
+
def info
|
21
|
+
output = if options[:website_only]
|
22
|
+
website
|
23
|
+
elsif options[:description_only]
|
24
|
+
description
|
25
|
+
else
|
26
|
+
description_and_website
|
27
|
+
end
|
28
|
+
output << "\n"
|
29
|
+
end
|
30
|
+
|
31
|
+
def should_insert?
|
32
|
+
!info.strip.empty? && !already_added_comment && !existing_comment_option
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def already_added_comment
|
38
|
+
prev_line_comment && prev_line_comment.include?(comment)
|
39
|
+
end
|
40
|
+
|
41
|
+
# if there exists a prev_line_comment and the user has specified new_comments_only
|
42
|
+
def existing_comment_option
|
43
|
+
prev_line_comment && options[:new_comments_only]
|
44
|
+
end
|
45
|
+
|
46
|
+
def leading_spaces_count
|
47
|
+
original_line.length - original_line.lstrip.length
|
48
|
+
end
|
49
|
+
|
50
|
+
def description
|
51
|
+
"#{spec.summary}" if spec
|
52
|
+
end
|
53
|
+
|
54
|
+
def website
|
55
|
+
"#{spec.homepage.to_s}" if spec
|
56
|
+
end
|
57
|
+
|
58
|
+
def description_and_website
|
59
|
+
output = "#{description}"
|
60
|
+
output << " (#{website})" unless website.nil? || website.empty?
|
61
|
+
output
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module AnnotateGem
|
2
|
+
class Gemfile
|
3
|
+
|
4
|
+
GEM_LINE_REGEX = /\A\s*gem[\s(]+["'](?<name>[^'"]*)["']/.freeze
|
5
|
+
|
6
|
+
attr_accessor :gemfile_path, :gem_lines, :source, :options
|
7
|
+
|
8
|
+
def initialize(gemfile_path, options = {})
|
9
|
+
@gemfile_path = gemfile_path
|
10
|
+
@source = []
|
11
|
+
@gem_lines = []
|
12
|
+
@options = options
|
13
|
+
end
|
14
|
+
|
15
|
+
def parse
|
16
|
+
self.source = File.readlines(gemfile_path)
|
17
|
+
source.each_with_index do |line, i|
|
18
|
+
if match = GEM_LINE_REGEX.match(line)
|
19
|
+
prev_line = source[i - 1] if i > 0
|
20
|
+
prev_line_comment = prev_line if is_line_a_comment?(prev_line)
|
21
|
+
self.gem_lines << GemLine.new(
|
22
|
+
name: match[:name],
|
23
|
+
original_line: line,
|
24
|
+
location: i,
|
25
|
+
prev_line_comment: prev_line_comment,
|
26
|
+
options: options
|
27
|
+
)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def write_comments
|
33
|
+
gem_lines.reverse.each do |gem_line|
|
34
|
+
source.insert(gem_line.location, gem_line.comment) if gem_line.should_insert?
|
35
|
+
end
|
36
|
+
File.write(gemfile_path, source.join)
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
def is_line_a_comment?(line)
|
42
|
+
line && line.strip.start_with?("#")
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
require "optparse"
|
2
|
+
|
3
|
+
module AnnotateGem
|
4
|
+
class Options
|
5
|
+
|
6
|
+
attr_accessor :options, :options_parser
|
7
|
+
|
8
|
+
def initialize
|
9
|
+
@options = {}
|
10
|
+
@options_parser = OptionParser.new do |opts|
|
11
|
+
add_banner(opts)
|
12
|
+
add_tail_options(opts)
|
13
|
+
add_info_options(opts)
|
14
|
+
add_gemfile_comment_options(opts)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def parse!(args)
|
19
|
+
options_parser.parse!(args)
|
20
|
+
validate_options
|
21
|
+
options
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def validate_options
|
27
|
+
info_flags = options[:website_only] && options[:description_only]
|
28
|
+
raise ArgumentError, "Cannot set both --website-only and --description-only flags" if info_flags
|
29
|
+
end
|
30
|
+
|
31
|
+
def add_info_options(opts)
|
32
|
+
opts.on("--website-only", "Only output the website") do
|
33
|
+
options[:website_only] = true
|
34
|
+
end
|
35
|
+
opts.on("--description-only", "Only output the description") do
|
36
|
+
options[:description_only] = true
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def add_gemfile_comment_options(opts)
|
41
|
+
opts.on("--new-comments-only", "Only add a comment to gemfile if there isn't a comment already") do
|
42
|
+
options[:new_comments_only] = true
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def add_banner(opts)
|
47
|
+
opts.banner = <<-BANNER.gsub(/\A\s{8}/, '')
|
48
|
+
#{AnnotateGem::DESCRIPTION}
|
49
|
+
Usage: #{opts.program_name} [options]
|
50
|
+
#{opts.program_name} [gem name]
|
51
|
+
BANNER
|
52
|
+
end
|
53
|
+
|
54
|
+
def add_tail_options(opts)
|
55
|
+
opts.on_tail("-h", "--help", "Show this message") do
|
56
|
+
puts opts
|
57
|
+
exit
|
58
|
+
end
|
59
|
+
opts.on_tail("-v", "--version", "Show version") do
|
60
|
+
puts AnnotateGem::VERSION
|
61
|
+
exit
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module AnnotateGem
|
2
|
+
module SpecFinder
|
3
|
+
extend self
|
4
|
+
|
5
|
+
def find_specs_for(gem_lines, &block)
|
6
|
+
gem_lines = Array(gem_lines)
|
7
|
+
find_matching_specs_for(gem_lines)
|
8
|
+
gems_to_fetch = gem_lines.select { |gem_line| gem_line.spec.nil? }
|
9
|
+
fetch_specs_for(gems_to_fetch, &block) if gems_to_fetch.any?
|
10
|
+
end
|
11
|
+
|
12
|
+
def find_matching_specs_for(gem_lines)
|
13
|
+
gem_lines.each do |line|
|
14
|
+
matching_specs = Gem::Dependency.new(line.name).matching_specs
|
15
|
+
# TODO: need to find latest
|
16
|
+
line.spec = matching_specs.first if matching_specs.any?
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def fetch_specs_for(gem_lines)
|
21
|
+
total = gem_lines.length
|
22
|
+
processing = 0
|
23
|
+
yield processing, total if block_given?
|
24
|
+
# TODO: Support private sources
|
25
|
+
remote = Bundler::Source::Rubygems::Remote.new(Gem.sources.first.uri)
|
26
|
+
fetcher = Bundler::Fetcher.new(remote)
|
27
|
+
dependency_fetcher = fetcher.fetchers.find {|f| Bundler::Fetcher::Dependency === f }
|
28
|
+
versions, _ = dependency_fetcher.dependency_specs(gem_lines.collect(&:name))
|
29
|
+
gem_lines.each do |gem_line|
|
30
|
+
processing += 1
|
31
|
+
yield processing, total if block_given?
|
32
|
+
gem_versions = versions.select { |v| v.first == gem_line.name }
|
33
|
+
next if gem_versions.empty? # couldn't find version on RubyGems so go to next one
|
34
|
+
version = find_latest_version(gem_versions)
|
35
|
+
gem_line.spec = fetcher.fetch_spec([gem_line.name, version])
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def find_latest_version(versions)
|
40
|
+
versions.sort_by { |v| v[1] }.last[1]
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require "test_helper"
|
2
|
+
require "annotate_gem/cli"
|
3
|
+
|
4
|
+
# Acts as integration test since it will actually fetch data from API
|
5
|
+
class AnnotateGem::CLITest < Minitest::Test
|
6
|
+
|
7
|
+
def test_run_for_gemfile
|
8
|
+
with_gemfile(File.read(gemfile_path)) do |path|
|
9
|
+
Bundler.expects(:default_gemfile).returns(path)
|
10
|
+
out, _ = capture_io do
|
11
|
+
AnnotateGem::CLI.new.run_for_gemfile
|
12
|
+
end
|
13
|
+
assert_equal File.read(annotated_gemfile_path), File.read(path)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def test_run_for_gem
|
18
|
+
out, _ = capture_io do
|
19
|
+
AnnotateGem::CLI.new.run_for_gem("annotate_gem")
|
20
|
+
end
|
21
|
+
assert_equal "Add comments to your Gemfile with each dependency's description. (https://github.com/ivantsepp/annotate_gem)\n", out
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
require "test_helper"
|
2
|
+
|
3
|
+
class AnnotateGem::GemLineTest < Minitest::Test
|
4
|
+
|
5
|
+
def test_info
|
6
|
+
gem_line = create_gem_line
|
7
|
+
spec = stub(summary: "Hello world", homepage: "http://example.com")
|
8
|
+
gem_line.spec = spec
|
9
|
+
assert_equal "Hello world (http://example.com)\n", gem_line.info
|
10
|
+
end
|
11
|
+
|
12
|
+
def test_info_with_website_only
|
13
|
+
gem_line = create_gem_line(options: { website_only: true })
|
14
|
+
spec = stub(summary: "Hello world", homepage: "http://example.com")
|
15
|
+
gem_line.spec = spec
|
16
|
+
assert_equal "http://example.com\n", gem_line.info
|
17
|
+
end
|
18
|
+
|
19
|
+
def test_info_with_description_only
|
20
|
+
gem_line = create_gem_line(options: { description_only: true })
|
21
|
+
spec = stub(summary: "Hello world", homepage: "http://example.com")
|
22
|
+
gem_line.spec = spec
|
23
|
+
assert_equal "Hello world\n", gem_line.info
|
24
|
+
end
|
25
|
+
|
26
|
+
def test_should_insert_with_no_info
|
27
|
+
gem_line = create_gem_line
|
28
|
+
gem_line.expects(:info).returns("")
|
29
|
+
refute gem_line.should_insert?
|
30
|
+
end
|
31
|
+
|
32
|
+
def test_should_insert_with_no_spec
|
33
|
+
gem_line = create_gem_line
|
34
|
+
refute gem_line.should_insert?
|
35
|
+
end
|
36
|
+
|
37
|
+
def test_should_insert_with_already_added_comment
|
38
|
+
gem_line = create_gem_line(prev_line_comment: "# Hello world (http://example.com)\n")
|
39
|
+
spec = stub(summary: "Hello world", homepage: "http://example.com")
|
40
|
+
gem_line.spec = spec
|
41
|
+
refute gem_line.should_insert?
|
42
|
+
end
|
43
|
+
|
44
|
+
def test_should_insert_with_new_comments_only
|
45
|
+
gem_line = create_gem_line(
|
46
|
+
prev_line_comment: "# This is an existing comment\n",
|
47
|
+
options: { new_comments_only: true }
|
48
|
+
)
|
49
|
+
spec = stub(summary: "Hello world", homepage: "http://example.com")
|
50
|
+
gem_line.spec = spec
|
51
|
+
refute gem_line.should_insert?
|
52
|
+
end
|
53
|
+
|
54
|
+
def test_comment
|
55
|
+
gem_line = create_gem_line
|
56
|
+
spec = stub(summary: "Hello world", homepage: "http://example.com")
|
57
|
+
gem_line.spec = spec
|
58
|
+
assert_equal "# Hello world (http://example.com)\n", gem_line.comment
|
59
|
+
end
|
60
|
+
|
61
|
+
def test_comment_with_indentation_in_original_line
|
62
|
+
gem_line = AnnotateGem::GemLine.new(
|
63
|
+
name: "annotate_gem",
|
64
|
+
original_line: " \tgem 'annotate_gem'"
|
65
|
+
)
|
66
|
+
spec = stub(summary: "Hello world", homepage: "http://example.com")
|
67
|
+
gem_line.spec = spec
|
68
|
+
assert_equal " \t# Hello world (http://example.com)\n", gem_line.comment
|
69
|
+
end
|
70
|
+
|
71
|
+
private
|
72
|
+
|
73
|
+
def create_gem_line(options = {})
|
74
|
+
AnnotateGem::GemLine.new(
|
75
|
+
{ name: "annotate_gem", original_line: "gem 'annotate_gem'" }.merge(options)
|
76
|
+
)
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
require "test_helper"
|
2
|
+
|
3
|
+
class AnnotateGem::GemfileTest < Minitest::Test
|
4
|
+
|
5
|
+
def test_parses_source
|
6
|
+
gemfile = gemfile_for(unindent(<<-GEMFILE))
|
7
|
+
hello
|
8
|
+
world!
|
9
|
+
GEMFILE
|
10
|
+
assert 2, gemfile.source.length
|
11
|
+
assert_equal "hello\n", gemfile.source.first
|
12
|
+
assert_equal "world!\n", gemfile.source.last
|
13
|
+
end
|
14
|
+
|
15
|
+
def test_parses_gem_lines
|
16
|
+
AnnotateGem::GemLine.expects(:new).with(
|
17
|
+
name: "rails",
|
18
|
+
original_line: "gem \"rails\"\n",
|
19
|
+
location: 0,
|
20
|
+
prev_line_comment: nil,
|
21
|
+
options: {}
|
22
|
+
).once
|
23
|
+
AnnotateGem::GemLine.expects(:new).with(
|
24
|
+
name: "annotate_gem",
|
25
|
+
original_line: "gem \"annotate_gem\"\n",
|
26
|
+
location: 2,
|
27
|
+
prev_line_comment: "# this gem has a comment\n",
|
28
|
+
options: {}
|
29
|
+
).once
|
30
|
+
gemfile = gemfile_for(unindent(<<-GEMFILE))
|
31
|
+
gem "rails"
|
32
|
+
# this gem has a comment
|
33
|
+
gem "annotate_gem"
|
34
|
+
# gem "commented_out"
|
35
|
+
GEMFILE
|
36
|
+
assert_equal 2, gemfile.gem_lines.length
|
37
|
+
end
|
38
|
+
|
39
|
+
def test_write_comments
|
40
|
+
with_gemfile("gem 'rails'") do |path|
|
41
|
+
gemfile = AnnotateGem::Gemfile.new(path)
|
42
|
+
gemfile.gem_lines = [mock(location: 0, comment: "Rails description!\n", should_insert?: true)]
|
43
|
+
gemfile.source = ["gem 'rails'"]
|
44
|
+
gemfile.write_comments
|
45
|
+
assert_equal "Rails description!\ngem 'rails'", File.read(path)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def test_that_options_are_passed_through
|
50
|
+
AnnotateGem::GemLine.expects(:new).with(
|
51
|
+
name: "rails",
|
52
|
+
original_line: "gem 'rails'",
|
53
|
+
location: 0,
|
54
|
+
prev_line_comment: nil,
|
55
|
+
options: { description_only: true }
|
56
|
+
).once
|
57
|
+
gemfile = gemfile_for("gem 'rails'", description_only: true)
|
58
|
+
end
|
59
|
+
|
60
|
+
private
|
61
|
+
|
62
|
+
def gemfile_for(content, options = {})
|
63
|
+
gemfile = nil
|
64
|
+
with_gemfile(content) do |path|
|
65
|
+
gemfile = AnnotateGem::Gemfile.new(path, options)
|
66
|
+
gemfile.parse
|
67
|
+
end
|
68
|
+
gemfile
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
require "test_helper"
|
2
|
+
|
3
|
+
class AnnotateGem::SpecFinderTest < Minitest::Test
|
4
|
+
def test_find_specs_for
|
5
|
+
gem_line_1 = mock(spec: nil)
|
6
|
+
gem_line_2 = mock(spec: "spec")
|
7
|
+
AnnotateGem::SpecFinder.expects(:find_matching_specs_for).with([gem_line_1, gem_line_2])
|
8
|
+
AnnotateGem::SpecFinder.expects(:fetch_specs_for).with([gem_line_1])
|
9
|
+
AnnotateGem::SpecFinder.find_specs_for([gem_line_1, gem_line_2])
|
10
|
+
end
|
11
|
+
|
12
|
+
def test_find_matching_specs_for
|
13
|
+
spec_1 = mock
|
14
|
+
gem_line_1 = mock(name: "1")
|
15
|
+
gem_line_1.expects(:spec=).with(spec_1)
|
16
|
+
spec_2 = mock
|
17
|
+
gem_line_2 = mock(name: "2")
|
18
|
+
gem_line_2.expects(:spec=).with(spec_2)
|
19
|
+
gem_lines = [gem_line_1, gem_line_2]
|
20
|
+
Gem::Dependency.expects(:new).with("1").returns(mock(matching_specs: [spec_1]))
|
21
|
+
Gem::Dependency.expects(:new).with("2").returns(mock(matching_specs: [spec_2]))
|
22
|
+
AnnotateGem::SpecFinder.find_matching_specs_for(gem_lines)
|
23
|
+
end
|
24
|
+
|
25
|
+
def test_fetch_specs_for
|
26
|
+
original_stdout = $stdout
|
27
|
+
$stdout = StringIO.new
|
28
|
+
|
29
|
+
spec_1 = mock
|
30
|
+
gem_line_1 = mock
|
31
|
+
gem_line_1.stubs(name: "1")
|
32
|
+
gem_line_1.expects(:spec=).with(spec_1)
|
33
|
+
spec_2 = mock
|
34
|
+
gem_line_2 = mock
|
35
|
+
gem_line_2.stubs(name: "2")
|
36
|
+
gem_line_2.expects(:spec=).with(spec_2)
|
37
|
+
|
38
|
+
dependency_fetcher_mock = Bundler::Fetcher::Dependency.new(nil, nil, nil)
|
39
|
+
Bundler::Fetcher.any_instance.expects(:fetchers).returns([dependency_fetcher_mock])
|
40
|
+
dependency_fetcher_mock.expects(:dependency_specs).with(["1", "2"]).returns([[
|
41
|
+
["1", Gem::Version.new("1")],
|
42
|
+
["2", Gem::Version.new("1")],
|
43
|
+
], "dependencies here"])
|
44
|
+
AnnotateGem::SpecFinder.expects(:find_latest_version).returns(Gem::Version.new("1")).twice
|
45
|
+
Bundler::Fetcher.any_instance.expects(:fetch_spec).with(["1", Gem::Version.new("1")]).returns(spec_1)
|
46
|
+
Bundler::Fetcher.any_instance.expects(:fetch_spec).with(["2", Gem::Version.new("1")]).returns(spec_2)
|
47
|
+
yielded_values = []
|
48
|
+
AnnotateGem::SpecFinder.fetch_specs_for([gem_line_1, gem_line_2]) do |*args|
|
49
|
+
yielded_values << args
|
50
|
+
end
|
51
|
+
assert_equal [[0, 2], [1, 2], [2, 2]], yielded_values
|
52
|
+
$stdout = original_stdout
|
53
|
+
end
|
54
|
+
|
55
|
+
def test_fetch_unknown_specs
|
56
|
+
original_stdout = $stdout
|
57
|
+
$stdout = StringIO.new
|
58
|
+
|
59
|
+
gem_line_1 = mock
|
60
|
+
gem_line_1.stubs(name: "1")
|
61
|
+
gem_line_1.expects(:spec=).never
|
62
|
+
|
63
|
+
dependency_fetcher_mock = Bundler::Fetcher::Dependency.new(nil, nil, nil)
|
64
|
+
Bundler::Fetcher.any_instance.expects(:fetchers).returns([dependency_fetcher_mock])
|
65
|
+
dependency_fetcher_mock.expects(:dependency_specs).with(["1"]).returns([[], "dependencies here"])
|
66
|
+
Bundler::Fetcher.any_instance.expects(:fetch_spec).never
|
67
|
+
AnnotateGem::SpecFinder.fetch_specs_for([gem_line_1])
|
68
|
+
|
69
|
+
$stdout = original_stdout
|
70
|
+
end
|
71
|
+
|
72
|
+
def test_find_latest_version
|
73
|
+
versions = [
|
74
|
+
["rails", Gem::Version.new("2.3.5")],
|
75
|
+
["rails", Gem::Version.new("4")]
|
76
|
+
]
|
77
|
+
assert_equal Gem::Version.new("4"), AnnotateGem::SpecFinder.find_latest_version(versions)
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
source "https://rubygems.org"
|
2
|
+
|
3
|
+
gem 'rails', '4.2.1'
|
4
|
+
gem 'sqlite3'
|
5
|
+
gem 'sass-rails', '~> 5.0'
|
6
|
+
gem 'uglifier', '>= 1.3.0'
|
7
|
+
gem 'coffee-rails', '~> 4.1.0'
|
8
|
+
# See https://github.com/rails/execjs#readme for more supported runtimes
|
9
|
+
# gem 'therubyracer', platforms: :ruby
|
10
|
+
|
11
|
+
gem 'jquery-rails'
|
12
|
+
gem 'turbolinks'
|
13
|
+
gem 'jbuilder', '~> 2.0'
|
14
|
+
# bundle exec rake doc:rails generates the API under doc/api.
|
15
|
+
gem 'sdoc', '~> 0.4.0', group: :doc
|
16
|
+
|
17
|
+
# Use ActiveModel has_secure_password
|
18
|
+
# gem 'bcrypt', '~> 3.1.7'
|
19
|
+
|
20
|
+
# Use Unicorn as the app server
|
21
|
+
# gem 'unicorn'
|
22
|
+
|
23
|
+
# Use Capistrano for deployment
|
24
|
+
# gem 'capistrano-rails', group: :development
|
25
|
+
|
26
|
+
group :development, :test do
|
27
|
+
gem 'byebug'
|
28
|
+
gem 'web-console', '~> 2.0'
|
29
|
+
# Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring
|
30
|
+
gem 'spring'
|
31
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
source "https://rubygems.org"
|
2
|
+
|
3
|
+
# Full-stack web application framework. (http://rubyonrails.org)
|
4
|
+
gem 'rails', '4.2.1'
|
5
|
+
# This module allows Ruby programs to interface with the SQLite3 database engine (http://www.sqlite.org) (https://github.com/sparklemotion/sqlite3-ruby)
|
6
|
+
gem 'sqlite3'
|
7
|
+
# Sass adapter for the Rails asset pipeline. (https://github.com/rails/sass-rails)
|
8
|
+
gem 'sass-rails', '~> 5.0'
|
9
|
+
# Ruby wrapper for UglifyJS JavaScript compressor (http://github.com/lautis/uglifier)
|
10
|
+
gem 'uglifier', '>= 1.3.0'
|
11
|
+
# CoffeeScript adapter for the Rails asset pipeline. (https://github.com/rails/coffee-rails)
|
12
|
+
gem 'coffee-rails', '~> 4.1.0'
|
13
|
+
# See https://github.com/rails/execjs#readme for more supported runtimes
|
14
|
+
# gem 'therubyracer', platforms: :ruby
|
15
|
+
|
16
|
+
# Use jQuery with Rails 4+ (http://rubygems.org/gems/jquery-rails)
|
17
|
+
gem 'jquery-rails'
|
18
|
+
# Turbolinks makes navigating your web application faster (https://github.com/turbolinks/turbolinks-rails)
|
19
|
+
gem 'turbolinks'
|
20
|
+
# Create JSON structures via a Builder-style DSL (https://github.com/rails/jbuilder)
|
21
|
+
gem 'jbuilder', '~> 2.0'
|
22
|
+
# bundle exec rake doc:rails generates the API under doc/api.
|
23
|
+
# rdoc html with javascript search index. (http://github.com/voloko/sdoc)
|
24
|
+
gem 'sdoc', '~> 0.4.0', group: :doc
|
25
|
+
|
26
|
+
# Use ActiveModel has_secure_password
|
27
|
+
# gem 'bcrypt', '~> 3.1.7'
|
28
|
+
|
29
|
+
# Use Unicorn as the app server
|
30
|
+
# gem 'unicorn'
|
31
|
+
|
32
|
+
# Use Capistrano for deployment
|
33
|
+
# gem 'capistrano-rails', group: :development
|
34
|
+
|
35
|
+
group :development, :test do
|
36
|
+
# Ruby 2.0 fast debugger - base + CLI (http://github.com/deivid-rodriguez/byebug)
|
37
|
+
gem 'byebug'
|
38
|
+
# A debugging tool for your Ruby on Rails applications. (https://github.com/rails/web-console)
|
39
|
+
gem 'web-console', '~> 2.0'
|
40
|
+
# Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring
|
41
|
+
# Rails application preloader (https://github.com/rails/spring)
|
42
|
+
gem 'spring'
|
43
|
+
end
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
require "minitest/autorun"
|
2
|
+
require "mocha/mini_test"
|
3
|
+
require "annotate_gem"
|
4
|
+
|
5
|
+
module TestHelpers
|
6
|
+
|
7
|
+
def gemfile_path
|
8
|
+
File.join(File.dirname(__FILE__), "fixtures", "Gemfile")
|
9
|
+
end
|
10
|
+
|
11
|
+
def annotated_gemfile_path
|
12
|
+
File.join(File.dirname(__FILE__), "fixtures", "Gemfile_annotated")
|
13
|
+
end
|
14
|
+
|
15
|
+
def with_gemfile(content)
|
16
|
+
file = Tempfile.new('gemfile')
|
17
|
+
file.write(content)
|
18
|
+
file.close
|
19
|
+
yield(file.path)
|
20
|
+
file.unlink
|
21
|
+
end
|
22
|
+
|
23
|
+
def unindent(str)
|
24
|
+
str.gsub(/^#{str.scan(/^[ ]+(?=\S)/).min}/, "")
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
|
29
|
+
class Minitest::Test
|
30
|
+
include TestHelpers
|
31
|
+
end
|
metadata
ADDED
@@ -0,0 +1,130 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: annotate_gem
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.8
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Ivan Tse
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2016-04-30 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 1.11.2
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 1.11.2
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '10.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '10.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: minitest
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '5.0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '5.0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: mocha
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '1.1'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '1.1'
|
69
|
+
description: 'Clarify your dependencies by adding a detailed comment to each line
|
70
|
+
in Gemfile specifying the gem''s summary and its website if any. '
|
71
|
+
email:
|
72
|
+
- ivan.tse1@gmail.com
|
73
|
+
executables:
|
74
|
+
- annotate_gem
|
75
|
+
extensions: []
|
76
|
+
extra_rdoc_files: []
|
77
|
+
files:
|
78
|
+
- ".gitignore"
|
79
|
+
- Gemfile
|
80
|
+
- LICENSE.txt
|
81
|
+
- README.md
|
82
|
+
- Rakefile
|
83
|
+
- annotate_gem.gemspec
|
84
|
+
- bin/annotate_gem
|
85
|
+
- lib/annotate_gem.rb
|
86
|
+
- lib/annotate_gem/cli.rb
|
87
|
+
- lib/annotate_gem/gem_line.rb
|
88
|
+
- lib/annotate_gem/gemfile.rb
|
89
|
+
- lib/annotate_gem/options.rb
|
90
|
+
- lib/annotate_gem/spec_finder.rb
|
91
|
+
- lib/annotate_gem/version.rb
|
92
|
+
- test/annotate_gem/cli_test.rb
|
93
|
+
- test/annotate_gem/gem_line_test.rb
|
94
|
+
- test/annotate_gem/gemfile_test.rb
|
95
|
+
- test/annotate_gem/spec_finder_test.rb
|
96
|
+
- test/fixtures/Gemfile
|
97
|
+
- test/fixtures/Gemfile_annotated
|
98
|
+
- test/test_helper.rb
|
99
|
+
homepage: https://github.com/ivantsepp/annotate_gem
|
100
|
+
licenses:
|
101
|
+
- MIT
|
102
|
+
metadata: {}
|
103
|
+
post_install_message:
|
104
|
+
rdoc_options: []
|
105
|
+
require_paths:
|
106
|
+
- lib
|
107
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
108
|
+
requirements:
|
109
|
+
- - ">="
|
110
|
+
- !ruby/object:Gem::Version
|
111
|
+
version: '0'
|
112
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
113
|
+
requirements:
|
114
|
+
- - ">="
|
115
|
+
- !ruby/object:Gem::Version
|
116
|
+
version: '0'
|
117
|
+
requirements: []
|
118
|
+
rubyforge_project:
|
119
|
+
rubygems_version: 2.6.3
|
120
|
+
signing_key:
|
121
|
+
specification_version: 4
|
122
|
+
summary: Add comments to your Gemfile with each dependency's description.
|
123
|
+
test_files:
|
124
|
+
- test/annotate_gem/cli_test.rb
|
125
|
+
- test/annotate_gem/gem_line_test.rb
|
126
|
+
- test/annotate_gem/gemfile_test.rb
|
127
|
+
- test/annotate_gem/spec_finder_test.rb
|
128
|
+
- test/fixtures/Gemfile
|
129
|
+
- test/fixtures/Gemfile_annotated
|
130
|
+
- test/test_helper.rb
|