gem-dependent 0.1.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.
- data/Gemfile +6 -0
- data/Gemfile.lock +34 -0
- data/Rakefile +20 -0
- data/Readme.md +42 -0
- data/VERSION +1 -0
- data/gem-dependent.gemspec +46 -0
- data/lib/rubygems/commands/dependent_command.rb +46 -0
- data/lib/rubygems/dependent.rb +89 -0
- data/lib/rubygems_plugin.rb +3 -0
- data/spec/dependent_spec.rb +56 -0
- data/spec/fixtures/gemcutter_specs.yml +9008 -0
- metadata +77 -0
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
GEM
|
2
|
+
remote: http://rubygems.org/
|
3
|
+
specs:
|
4
|
+
diff-lcs (1.1.2)
|
5
|
+
gemcutter (0.6.1)
|
6
|
+
git (1.2.5)
|
7
|
+
jeweler (1.4.0)
|
8
|
+
gemcutter (>= 0.1.0)
|
9
|
+
git (>= 1.2.5)
|
10
|
+
rubyforge (>= 2.0.0)
|
11
|
+
json_pure (1.4.6)
|
12
|
+
parallel (0.5.0)
|
13
|
+
rake (0.8.7)
|
14
|
+
rspec (2.0.1)
|
15
|
+
rspec-core (~> 2.0.1)
|
16
|
+
rspec-expectations (~> 2.0.1)
|
17
|
+
rspec-mocks (~> 2.0.1)
|
18
|
+
rspec-core (2.0.1)
|
19
|
+
rspec-expectations (2.0.1)
|
20
|
+
diff-lcs (>= 1.1.2)
|
21
|
+
rspec-mocks (2.0.1)
|
22
|
+
rspec-core (~> 2.0.1)
|
23
|
+
rspec-expectations (~> 2.0.1)
|
24
|
+
rubyforge (2.0.4)
|
25
|
+
json_pure (>= 1.1.7)
|
26
|
+
|
27
|
+
PLATFORMS
|
28
|
+
ruby
|
29
|
+
|
30
|
+
DEPENDENCIES
|
31
|
+
jeweler
|
32
|
+
parallel
|
33
|
+
rake
|
34
|
+
rspec (~> 2)
|
data/Rakefile
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
task :default => :spec
|
2
|
+
require "rspec/core/rake_task"
|
3
|
+
RSpec::Core::RakeTask.new(:spec) do |t|
|
4
|
+
t.rspec_opts = '--backtrace --color'
|
5
|
+
end
|
6
|
+
|
7
|
+
begin
|
8
|
+
require 'jeweler'
|
9
|
+
Jeweler::Tasks.new do |gem|
|
10
|
+
gem.name = 'gem-dependent'
|
11
|
+
gem.summary = "See which gems depend on your gems"
|
12
|
+
gem.email = "grosser.michael@gmail.com"
|
13
|
+
gem.homepage = "http://github.com/grosser/#{gem.name}"
|
14
|
+
gem.authors = ["Michael Grosser"]
|
15
|
+
end
|
16
|
+
|
17
|
+
Jeweler::GemcutterTasks.new
|
18
|
+
rescue LoadError
|
19
|
+
puts "Jeweler, or one of its dependencies, is not available. Install it with: gem install jeweler"
|
20
|
+
end
|
data/Readme.md
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
Install
|
2
|
+
=======
|
3
|
+
sudo gem install gem-dependent
|
4
|
+
|
5
|
+
Usage
|
6
|
+
=====
|
7
|
+
gem dependent my_gem
|
8
|
+
|
9
|
+
--source URL Query this source (e.g. http://rubygems.org)
|
10
|
+
--no-progress Do not show progress
|
11
|
+
--fetch-limit N Fetch specs for max N gems (for fast debugging)
|
12
|
+
|
13
|
+
Output
|
14
|
+
======
|
15
|
+
|
16
|
+
$ gem dependent my_gem --source http://rubygems.org
|
17
|
+
other_gem >= 1.2.1
|
18
|
+
even_more = 0.0.1
|
19
|
+
|
20
|
+
$ gem dependent XXX --source http://rubygems.org --no-progress | wc -l
|
21
|
+
|
22
|
+
# Fun-facts from 2010-11-03
|
23
|
+
bundler: 263
|
24
|
+
activesupport: 983
|
25
|
+
activerecord: 566
|
26
|
+
|
27
|
+
hoe: 1454
|
28
|
+
jeweler: 234
|
29
|
+
echoe: 85
|
30
|
+
|
31
|
+
nokogiri: 516
|
32
|
+
hpricot: 297
|
33
|
+
|
34
|
+
TODO
|
35
|
+
=====
|
36
|
+
- include reverse dependencies (a > b > c --> a = [b,c])
|
37
|
+
|
38
|
+
Author
|
39
|
+
======
|
40
|
+
[Michael Grosser](http://grosser.it)
|
41
|
+
grosser.michael@gmail.com
|
42
|
+
Hereby placed under public domain, do what you want, just do not hold me accountable...
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.1.0
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = %q{gem-dependent}
|
8
|
+
s.version = "0.1.0"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Michael Grosser"]
|
12
|
+
s.date = %q{2010-11-03}
|
13
|
+
s.email = %q{grosser.michael@gmail.com}
|
14
|
+
s.files = [
|
15
|
+
"Gemfile",
|
16
|
+
"Gemfile.lock",
|
17
|
+
"Rakefile",
|
18
|
+
"Readme.md",
|
19
|
+
"VERSION",
|
20
|
+
"gem-dependent.gemspec",
|
21
|
+
"lib/rubygems/commands/dependent_command.rb",
|
22
|
+
"lib/rubygems/dependent.rb",
|
23
|
+
"lib/rubygems_plugin.rb",
|
24
|
+
"spec/dependent_spec.rb",
|
25
|
+
"spec/fixtures/gemcutter_specs.yml"
|
26
|
+
]
|
27
|
+
s.homepage = %q{http://github.com/grosser/gem-dependent}
|
28
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
29
|
+
s.require_paths = ["lib"]
|
30
|
+
s.rubygems_version = %q{1.3.7}
|
31
|
+
s.summary = %q{See which gems depend on your gems}
|
32
|
+
s.test_files = [
|
33
|
+
"spec/dependent_spec.rb"
|
34
|
+
]
|
35
|
+
|
36
|
+
if s.respond_to? :specification_version then
|
37
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
38
|
+
s.specification_version = 3
|
39
|
+
|
40
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
41
|
+
else
|
42
|
+
end
|
43
|
+
else
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'rubygems/command'
|
2
|
+
require 'rubygems/dependent'
|
3
|
+
|
4
|
+
class Gem::Commands::DependentCommand < Gem::Command
|
5
|
+
def initialize
|
6
|
+
super 'dependent', 'Show which gems depend on a gem', :progress => true
|
7
|
+
|
8
|
+
add_option('--source URL', 'Query these sources (e.g. http://gemcutter.org)') do |remote, _|
|
9
|
+
options[:source] = remote.to_s.split(',')
|
10
|
+
end
|
11
|
+
|
12
|
+
add_option('--no-progress', 'Do not show progress') do
|
13
|
+
options[:progress] = false
|
14
|
+
end
|
15
|
+
|
16
|
+
add_option('--fetch-limit N', Integer, 'Fetch specs for max N gems (for fast debugging)') do |limit, _|
|
17
|
+
options[:fetch_limit] = limit
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def arguments
|
22
|
+
"GEMNAME which gems depend on a gem"
|
23
|
+
end
|
24
|
+
|
25
|
+
def usage
|
26
|
+
"#{program_name} GEMNAME"
|
27
|
+
end
|
28
|
+
|
29
|
+
def execute
|
30
|
+
gem = get_all_gem_names.first
|
31
|
+
gems_and_dependencies = Gem::Dependent.find(gem, options)
|
32
|
+
gems_and_dependencies.each do |gem, dependencies|
|
33
|
+
requirements = dependencies.map do |dependency|
|
34
|
+
formatted_dependency(dependency)
|
35
|
+
end.join(', ')
|
36
|
+
puts "#{gem} #{requirements}"
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
def formatted_dependency(dependency)
|
43
|
+
type = " (#{dependency.type})" if dependency.type == :development
|
44
|
+
"#{dependency.requirement}#{type}"
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
require 'parallel'
|
2
|
+
require 'rubygems/spec_fetcher'
|
3
|
+
|
4
|
+
module Gem
|
5
|
+
class Dependent
|
6
|
+
VERSION = File.read( File.join(File.dirname(__FILE__),'..','..','VERSION') ).strip
|
7
|
+
|
8
|
+
def self.find(gem, options={})
|
9
|
+
specs_and_sources = with_changed_source(options[:source]) do
|
10
|
+
all_specs_and_sources
|
11
|
+
end
|
12
|
+
|
13
|
+
if options[:fetch_limit]
|
14
|
+
specs_and_sources = specs_and_sources.first(options[:fetch_limit])
|
15
|
+
end
|
16
|
+
|
17
|
+
puts "Downloading specs for #{specs_and_sources.size} gems" if options[:progress]
|
18
|
+
|
19
|
+
# fetch dependencies
|
20
|
+
gem_names_and_dependencies = Parallel.map(specs_and_sources, :in_processes => 20) do |spec_tuple, source_uri|
|
21
|
+
print '.' if options[:progress]
|
22
|
+
name = spec_tuple.first
|
23
|
+
dependencies = dependencies(spec_tuple, source_uri)
|
24
|
+
[name, dependencies]
|
25
|
+
end
|
26
|
+
|
27
|
+
print "\n" if options[:progress]
|
28
|
+
|
29
|
+
# select those that depend on #{gem}
|
30
|
+
gem_names_and_dependencies.map do |_, dependencies|
|
31
|
+
found = dependencies.select{|d| d.name == gem}
|
32
|
+
next if found.empty?
|
33
|
+
[_, found]
|
34
|
+
end.compact
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
# dependencies for given gem
|
40
|
+
def self.dependencies(spec_tuple, source_uri)
|
41
|
+
begin
|
42
|
+
fetcher = Gem::SpecFetcher.fetcher
|
43
|
+
fetcher.fetch_spec(spec_tuple, URI.parse(source_uri)).dependencies
|
44
|
+
rescue Gem::RemoteFetcher::FetchError, Zlib::DataError => e
|
45
|
+
$stderr.puts e
|
46
|
+
[]
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def self.all_specs_and_sources
|
51
|
+
fetcher = Gem::SpecFetcher.fetcher
|
52
|
+
all = true
|
53
|
+
matching_platform = false
|
54
|
+
prerelease = false
|
55
|
+
name = Gem::Dependency.new(//i, Gem::Requirement.default)
|
56
|
+
specs_and_sources = fetcher.find_matching name, all, matching_platform, prerelease
|
57
|
+
uniq_by(specs_and_sources){|a|a.first.first}
|
58
|
+
end
|
59
|
+
|
60
|
+
# get unique elements from an array (last found is used)
|
61
|
+
# http://drawohara.com/post/146659159/ruby-enumerable-uniq-by
|
62
|
+
def self.uniq_by(array, &block)
|
63
|
+
uniq = {}
|
64
|
+
array.each_with_index do |val, idx|
|
65
|
+
key = block.call(val)
|
66
|
+
uniq[key] = [idx, val]
|
67
|
+
end
|
68
|
+
values = uniq.values
|
69
|
+
values.sort!{|a,b| a.first <=> b.first}
|
70
|
+
values.map!{|pair| pair.last}
|
71
|
+
values
|
72
|
+
end
|
73
|
+
|
74
|
+
def self.with_changed_source(sources)
|
75
|
+
sources = [*sources].compact
|
76
|
+
if sources.empty?
|
77
|
+
yield
|
78
|
+
else
|
79
|
+
begin
|
80
|
+
old = Gem.sources
|
81
|
+
Gem.sources = sources
|
82
|
+
yield
|
83
|
+
ensure
|
84
|
+
Gem.sources = old
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
$LOAD_PATH.unshift File.expand_path('../lib', File.dirname(__FILE__))
|
2
|
+
require 'lib/rubygems/dependent' # normal require does not work once gem is installed, wtf...
|
3
|
+
|
4
|
+
describe Gem::Dependent do
|
5
|
+
before do
|
6
|
+
Gem::SpecFetcher.fetcher = nil # reset cache
|
7
|
+
end
|
8
|
+
|
9
|
+
let(:fixture){ YAML.load(File.read('spec/fixtures/gemcutter_specs.yml')) }
|
10
|
+
let(:hoe_gems){
|
11
|
+
[
|
12
|
+
["7digital", ["hoe"]],
|
13
|
+
["abingo_port", ["hoe"]],
|
14
|
+
["abundance", ["hoe"]],
|
15
|
+
["actionview-data", ["hoe"]],
|
16
|
+
["active_link_to", ["hoe"]],
|
17
|
+
["activemerchant-paymentech-orbital", ["hoe"]],
|
18
|
+
["active_nomad", ["hoe"]],
|
19
|
+
["active_presenter", ["hoe"]],
|
20
|
+
["activerecord-fast-import", ["hoe"]],
|
21
|
+
["activerecord-jdbc-adapter", ["hoe"]]
|
22
|
+
]
|
23
|
+
}
|
24
|
+
|
25
|
+
def simplify(dependencies)
|
26
|
+
dependencies.map{|name, deps| [name, deps.map{|d| d.name}] }
|
27
|
+
end
|
28
|
+
|
29
|
+
def stub_source(gem_source = nil)
|
30
|
+
gem_source ||= 'http://gemcutter.org'
|
31
|
+
Gem::SpecFetcher.fetcher.should_receive(:load_specs).with(URI.parse(gem_source), 'specs').and_return(fixture)
|
32
|
+
Gem.sources = [gem_source]
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'finds dependencies for given gem' do
|
36
|
+
stub_source
|
37
|
+
dependencies = simplify(Gem::Dependent.find('hoe'))
|
38
|
+
dependencies.should == hoe_gems
|
39
|
+
end
|
40
|
+
|
41
|
+
it "obeys fetch-limit" do
|
42
|
+
stub_source
|
43
|
+
dependencies = simplify(Gem::Dependent.find('hoe', :fetch_limit => 100))
|
44
|
+
dependencies.should == hoe_gems.first(4)
|
45
|
+
end
|
46
|
+
|
47
|
+
it "can use given source" do
|
48
|
+
source = 'http://rubygems.org'
|
49
|
+
stub_source(source)
|
50
|
+
Gem::Dependent.find('hoe', :source => source)
|
51
|
+
end
|
52
|
+
|
53
|
+
it "has a VERSION" do
|
54
|
+
Gem::Dependent::VERSION.should =~ /^\d+\.\d+\.\d+$/
|
55
|
+
end
|
56
|
+
end
|