gemirro 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of gemirro might be problematic. Click here for more details.
- checksums.yaml +15 -0
- data/.gitignore +5 -0
- data/.rubocop.yml +24 -0
- data/.travis.yml +7 -0
- data/Gemfile +4 -0
- data/LICENSE +674 -0
- data/MANIFEST +46 -0
- data/README.md +60 -0
- data/Rakefile +10 -0
- data/bin/gemirro +6 -0
- data/gemirro.gemspec +31 -0
- data/lib/gemirro.rb +37 -0
- data/lib/gemirro/cli.rb +63 -0
- data/lib/gemirro/cli/index.rb +24 -0
- data/lib/gemirro/cli/init.rb +17 -0
- data/lib/gemirro/cli/server.rb +19 -0
- data/lib/gemirro/cli/update.rb +18 -0
- data/lib/gemirro/configuration.rb +137 -0
- data/lib/gemirro/gem.rb +70 -0
- data/lib/gemirro/gems_fetcher.rb +126 -0
- data/lib/gemirro/http.rb +37 -0
- data/lib/gemirro/indexer.rb +56 -0
- data/lib/gemirro/mirror_directory.rb +59 -0
- data/lib/gemirro/mirror_file.rb +48 -0
- data/lib/gemirro/server.rb +164 -0
- data/lib/gemirro/source.rb +58 -0
- data/lib/gemirro/version.rb +5 -0
- data/lib/gemirro/versions_fetcher.rb +31 -0
- data/lib/gemirro/versions_file.rb +65 -0
- data/spec/gemirro/cli_spec.rb +51 -0
- data/spec/gemirro/configuration_spec.rb +88 -0
- data/spec/gemirro/gem_spec.rb +37 -0
- data/spec/gemirro/gems_fetcher_spec.rb +104 -0
- data/spec/gemirro/http_spec.rb +36 -0
- data/spec/gemirro/indexer_spec.rb +55 -0
- data/spec/gemirro/mirror_directory_spec.rb +37 -0
- data/spec/gemirro/mirror_file_spec.rb +23 -0
- data/spec/gemirro/server_spec.rb +96 -0
- data/spec/gemirro/source_spec.rb +44 -0
- data/spec/gemirro/versions_fetcher_spec.rb +25 -0
- data/spec/gemirro/versions_file_spec.rb +52 -0
- data/spec/spec_helper.rb +17 -0
- data/task/manifest.rake +9 -0
- data/task/rspec.rake +6 -0
- data/task/rubocop.rake +5 -0
- data/template/config.rb +25 -0
- data/template/public/gems/.gitkeep +0 -0
- metadata +230 -0
data/lib/gemirro/gem.rb
ADDED
@@ -0,0 +1,70 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
module Gemirro
|
3
|
+
##
|
4
|
+
# The Gem class contains data about a Gem such as the name, requirement as
|
5
|
+
# well as providing some methods to more easily extract the specific version
|
6
|
+
# number.
|
7
|
+
#
|
8
|
+
# @!attribute [r] name
|
9
|
+
# @return [String]
|
10
|
+
# @!attribute [r] requirement
|
11
|
+
# @return [Gem::Requirement]
|
12
|
+
#
|
13
|
+
class Gem
|
14
|
+
attr_reader :name, :requirement
|
15
|
+
|
16
|
+
##
|
17
|
+
# Returns a `Gem::Version` instance based on the specified requirement.
|
18
|
+
#
|
19
|
+
# @param [Gem::Requirement] requirement
|
20
|
+
# @return [Gem::Version]
|
21
|
+
#
|
22
|
+
def self.version_for(requirement)
|
23
|
+
::Gem::Version.new(requirement.requirements.sort.last.last.version)
|
24
|
+
end
|
25
|
+
|
26
|
+
##
|
27
|
+
# @param [String] name
|
28
|
+
# @param [Gem::Requirement|String] requirement
|
29
|
+
#
|
30
|
+
def initialize(name, requirement = nil)
|
31
|
+
requirement ||= ::Gem::Requirement.default
|
32
|
+
|
33
|
+
if requirement.is_a?(String)
|
34
|
+
requirement = ::Gem::Requirement.new(requirement)
|
35
|
+
end
|
36
|
+
|
37
|
+
@name = name
|
38
|
+
@requirement = requirement
|
39
|
+
end
|
40
|
+
|
41
|
+
##
|
42
|
+
# Returns the version
|
43
|
+
#
|
44
|
+
# @return [Gem::Version]
|
45
|
+
#
|
46
|
+
def version
|
47
|
+
@version ||= self.class.version_for(requirement)
|
48
|
+
end
|
49
|
+
|
50
|
+
##
|
51
|
+
# Define if version exists
|
52
|
+
#
|
53
|
+
# @return [TrueClass|FalseClass]
|
54
|
+
#
|
55
|
+
def version?
|
56
|
+
version && !version.segments.reject { |s| s == 0 }.empty?
|
57
|
+
end
|
58
|
+
|
59
|
+
##
|
60
|
+
# Returns the filename of the Gemfile.
|
61
|
+
#
|
62
|
+
# @param [String] gem_version
|
63
|
+
# @return [String]
|
64
|
+
#
|
65
|
+
def filename(gem_version = nil)
|
66
|
+
gem_version ||= version.to_s
|
67
|
+
"#{name}-#{gem_version}.gem"
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,126 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
module Gemirro
|
3
|
+
##
|
4
|
+
# The GemsFetcher class is responsible for downloading Gems from an external
|
5
|
+
# source.
|
6
|
+
#
|
7
|
+
# @!attribute [r] source
|
8
|
+
# @return [Source]
|
9
|
+
# @!attribute [r] versions_file
|
10
|
+
# @return [Gemirro::VersionsFile]
|
11
|
+
#
|
12
|
+
class GemsFetcher
|
13
|
+
attr_reader :source, :versions_file
|
14
|
+
|
15
|
+
##
|
16
|
+
# @param [Source] source
|
17
|
+
# @param [Gemirro::VersionsFile] versions_file
|
18
|
+
#
|
19
|
+
def initialize(source, versions_file)
|
20
|
+
@source = source
|
21
|
+
@versions_file = versions_file
|
22
|
+
end
|
23
|
+
|
24
|
+
##
|
25
|
+
# Fetches the Gems.
|
26
|
+
#
|
27
|
+
def fetch
|
28
|
+
@source.gems.each do |gem|
|
29
|
+
versions_for(gem).each do |version|
|
30
|
+
filename = gem.filename(version)
|
31
|
+
satisfied = gem.requirement.satisfied_by?(version)
|
32
|
+
name = gem.name
|
33
|
+
|
34
|
+
if gem_exists?(filename) || ignore_gem?(name, version) || !satisfied
|
35
|
+
logger.debug("Skipping #{filename}")
|
36
|
+
next
|
37
|
+
end
|
38
|
+
|
39
|
+
configuration.ignore_gem(gem.name, version)
|
40
|
+
logger.info("Fetching #{filename}")
|
41
|
+
gemfile = fetch_gem(gem, version)
|
42
|
+
configuration.mirror_directory.add_file(filename, gemfile) if gemfile
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
##
|
48
|
+
# Returns an Array containing the versions that should be fetched for a
|
49
|
+
# Gem.
|
50
|
+
#
|
51
|
+
# @param [Gemirro::Gem] gem
|
52
|
+
# @return [Array]
|
53
|
+
#
|
54
|
+
def versions_for(gem)
|
55
|
+
available = @versions_file.versions_for(gem.name)
|
56
|
+
versions = gem.version? ? [gem.version] : available
|
57
|
+
available_names = available.map(&:to_s)
|
58
|
+
|
59
|
+
# Get rid of invalid versions. Due to Gem::Version having a custom ==
|
60
|
+
# method, which treats "3.4" the same as "3.4.0" we'll have to compare
|
61
|
+
# the versions as String instances.
|
62
|
+
versions = versions.select do |version|
|
63
|
+
available_names.include?(version.to_s)
|
64
|
+
end
|
65
|
+
|
66
|
+
versions = [available.last] if versions.empty?
|
67
|
+
|
68
|
+
versions
|
69
|
+
end
|
70
|
+
|
71
|
+
##
|
72
|
+
# Tries to download the Gemfile for the specified Gem and version.
|
73
|
+
#
|
74
|
+
# @param [Gemirro::Gem] gem
|
75
|
+
# @param [Gem::Version] version
|
76
|
+
# @return [String]
|
77
|
+
#
|
78
|
+
def fetch_gem(gem, version)
|
79
|
+
data = nil
|
80
|
+
filename = gem.filename(version)
|
81
|
+
|
82
|
+
begin
|
83
|
+
data = @source.fetch_gem(gem.name, version)
|
84
|
+
rescue => e
|
85
|
+
logger.error("Failed to retrieve #{filename}: #{e.message}")
|
86
|
+
logger.debug("Adding #{filename} to the list of ignored Gems")
|
87
|
+
|
88
|
+
configuration.ignore_gem(gem.name, version)
|
89
|
+
end
|
90
|
+
|
91
|
+
data
|
92
|
+
end
|
93
|
+
|
94
|
+
##
|
95
|
+
# @see Gemirro::Configuration#logger
|
96
|
+
# @return [Logger]
|
97
|
+
#
|
98
|
+
def logger
|
99
|
+
configuration.logger
|
100
|
+
end
|
101
|
+
|
102
|
+
##
|
103
|
+
# @see Gemirro.configuration
|
104
|
+
#
|
105
|
+
def configuration
|
106
|
+
Gemirro.configuration
|
107
|
+
end
|
108
|
+
|
109
|
+
##
|
110
|
+
# Checks if a given Gem has already been downloaded.
|
111
|
+
#
|
112
|
+
# @param [String] filename
|
113
|
+
# @return [TrueClass|FalseClass]
|
114
|
+
#
|
115
|
+
def gem_exists?(filename)
|
116
|
+
configuration.mirror_directory.file_exists?(filename)
|
117
|
+
end
|
118
|
+
|
119
|
+
##
|
120
|
+
# @see Gemirro::Configuration#ignore_gem?
|
121
|
+
#
|
122
|
+
def ignore_gem?(*args)
|
123
|
+
configuration.ignore_gem?(*args)
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
data/lib/gemirro/http.rb
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
|
3
|
+
module Gemirro
|
4
|
+
##
|
5
|
+
# The Http class is responsible for executing GET request
|
6
|
+
# to a specific url and return an response as an HTTP::Message
|
7
|
+
#
|
8
|
+
# @!attribute [r] client
|
9
|
+
# @return [HTTPClient]
|
10
|
+
#
|
11
|
+
class Http
|
12
|
+
attr_accessor :client
|
13
|
+
|
14
|
+
##
|
15
|
+
# Requests the given HTTP resource.
|
16
|
+
#
|
17
|
+
# @param [String] url
|
18
|
+
# @return [HTTP::Message]
|
19
|
+
#
|
20
|
+
def self.get(url)
|
21
|
+
response = client.get(url, follow_redirect: true)
|
22
|
+
|
23
|
+
unless HTTP::Status.successful?(response.status)
|
24
|
+
fail HTTPClient::BadResponseError, response.reason
|
25
|
+
end
|
26
|
+
|
27
|
+
response
|
28
|
+
end
|
29
|
+
|
30
|
+
##
|
31
|
+
# @return [HTTPClient]
|
32
|
+
#
|
33
|
+
def self.client
|
34
|
+
@client ||= HTTPClient.new
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
|
3
|
+
module Gemirro
|
4
|
+
##
|
5
|
+
# The Indexer class is responsible for downloading useful file directly
|
6
|
+
# on the source host, such as specs-*.*.gz, marshal information, etc...
|
7
|
+
#
|
8
|
+
# @!attribute [r] files
|
9
|
+
# @return [Array]
|
10
|
+
# @!attribute [r] quick_marshal_dir
|
11
|
+
# @return [String]
|
12
|
+
# @!attribute [r] directory
|
13
|
+
# @return [String]
|
14
|
+
# @!attribute [r] dest_directory
|
15
|
+
# @return [String]
|
16
|
+
#
|
17
|
+
class Indexer < ::Gem::Indexer
|
18
|
+
attr_accessor :files, :quick_marshal_dir, :directory, :dest_directory
|
19
|
+
|
20
|
+
##
|
21
|
+
# Generate indicies on the destination directory
|
22
|
+
#
|
23
|
+
# @return [Array]
|
24
|
+
#
|
25
|
+
def install_indicies
|
26
|
+
verbose = ::Gem.configuration.really_verbose
|
27
|
+
say "Downloading index into production dir #{@dest_directory}" if verbose
|
28
|
+
|
29
|
+
files = @files
|
30
|
+
files.delete @quick_marshal_dir if files.include? @quick_dir
|
31
|
+
|
32
|
+
if files.include?(@quick_marshal_dir) && !files.include?(@quick_dir)
|
33
|
+
files.delete @quick_marshal_dir
|
34
|
+
dst_name = File.join(@dest_directory, @quick_marshal_dir_base)
|
35
|
+
FileUtils.mkdir_p(File.dirname(dst_name), verbose: verbose)
|
36
|
+
FileUtils.rm_rf(dst_name, verbose: verbose)
|
37
|
+
FileUtils.mv(@quick_marshal_dir, dst_name,
|
38
|
+
verbose: verbose, force: true)
|
39
|
+
end
|
40
|
+
|
41
|
+
files = files.map do |path|
|
42
|
+
path.sub(/^#{Regexp.escape @directory}\/?/, '')
|
43
|
+
end
|
44
|
+
|
45
|
+
files.each do |file|
|
46
|
+
dst_name = File.join @dest_directory, file
|
47
|
+
next if File.exist?(dst_name) &&
|
48
|
+
(File.mtime(dst_name) >= Time.now - 360)
|
49
|
+
|
50
|
+
resp = Http.get("#{Gemirro.configuration.source.host}/#{file}")
|
51
|
+
next unless resp.code == 200
|
52
|
+
MirrorFile.new(dst_name).write(resp.body)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
module Gemirro
|
3
|
+
##
|
4
|
+
# The MirrorDirectory is used for dealing with files and directories that are
|
5
|
+
# mirrored from an external source.
|
6
|
+
#
|
7
|
+
# @!attribute [r] path
|
8
|
+
# @return [String]
|
9
|
+
#
|
10
|
+
class MirrorDirectory
|
11
|
+
attr_reader :path
|
12
|
+
|
13
|
+
##
|
14
|
+
# @param [String] path
|
15
|
+
#
|
16
|
+
def initialize(path)
|
17
|
+
@path = path
|
18
|
+
end
|
19
|
+
|
20
|
+
##
|
21
|
+
# Creates directory or directories with the given path.
|
22
|
+
#
|
23
|
+
# @param [String] dir_path
|
24
|
+
# @return [Gemirro::MirrorDirectory]
|
25
|
+
#
|
26
|
+
def add_directory(dir_path)
|
27
|
+
full_path = File.join(@path, dir_path)
|
28
|
+
FileUtils.mkdir_p(full_path) unless File.directory?(full_path)
|
29
|
+
|
30
|
+
self.class.new(full_path)
|
31
|
+
end
|
32
|
+
|
33
|
+
##
|
34
|
+
# Creates a new file with the given name and content.
|
35
|
+
#
|
36
|
+
# @param [String] name
|
37
|
+
# @param [String] content
|
38
|
+
# @return [Gem::MirrorFile]
|
39
|
+
#
|
40
|
+
def add_file(name, content)
|
41
|
+
full_path = File.join(@path, name)
|
42
|
+
file = MirrorFile.new(full_path)
|
43
|
+
|
44
|
+
file.write(content)
|
45
|
+
|
46
|
+
file
|
47
|
+
end
|
48
|
+
|
49
|
+
##
|
50
|
+
# Checks if a given file exists in the current directory.
|
51
|
+
#
|
52
|
+
# @param [String] name
|
53
|
+
# @return [TrueClass|FalseClass]
|
54
|
+
#
|
55
|
+
def file_exists?(name)
|
56
|
+
File.file?(File.join(@path, name))
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
module Gemirro
|
3
|
+
##
|
4
|
+
# Similar to {Gemirro::MirrorDirectory} the MirrorFile class is used to
|
5
|
+
# make it easier to read and write data in a directory that mirrors data from
|
6
|
+
# an external source.
|
7
|
+
#
|
8
|
+
# @!attribute [r] path
|
9
|
+
# @return [String]
|
10
|
+
#
|
11
|
+
class MirrorFile
|
12
|
+
attr_reader :path
|
13
|
+
|
14
|
+
##
|
15
|
+
# @param [String] path
|
16
|
+
#
|
17
|
+
def initialize(path)
|
18
|
+
@path = path
|
19
|
+
end
|
20
|
+
|
21
|
+
##
|
22
|
+
# Writes the specified content to the current file. Existing files are
|
23
|
+
# overwritten.
|
24
|
+
#
|
25
|
+
# @param [String] content
|
26
|
+
#
|
27
|
+
def write(content)
|
28
|
+
handle = File.open(@path, 'w')
|
29
|
+
|
30
|
+
handle.write(content)
|
31
|
+
handle.close
|
32
|
+
end
|
33
|
+
|
34
|
+
##
|
35
|
+
# Reads the content of the current file.
|
36
|
+
#
|
37
|
+
# @return [String]
|
38
|
+
#
|
39
|
+
def read
|
40
|
+
handle = File.open(@path, 'r')
|
41
|
+
content = handle.read
|
42
|
+
|
43
|
+
handle.close
|
44
|
+
|
45
|
+
content
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,164 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
|
3
|
+
module Gemirro
|
4
|
+
##
|
5
|
+
# Launch TCPServer to easily download gems.
|
6
|
+
#
|
7
|
+
# @!attribute [r] server
|
8
|
+
# @return [TCPServer]
|
9
|
+
# @!attribute [r] destination
|
10
|
+
# @return [String]
|
11
|
+
# @!attribute [r] versions_fetcher
|
12
|
+
# @return [VersionsFetcher]
|
13
|
+
# @!attribute [r] gems_fetcher
|
14
|
+
# @return [Gemirro::GemsFetcher]
|
15
|
+
#
|
16
|
+
class Server
|
17
|
+
attr_reader :server, :destination, :versions_fetcher, :gems_fetcher
|
18
|
+
|
19
|
+
##
|
20
|
+
# Initialize Server
|
21
|
+
#
|
22
|
+
def initialize
|
23
|
+
configuration.server_host = 'localhost' if configuration.server_host.nil?
|
24
|
+
configuration.server_port = '2000' if configuration.server_port.nil?
|
25
|
+
logger.info('Running server on ' \
|
26
|
+
"#{configuration.server_host}:#{configuration.server_port}")
|
27
|
+
@server = TCPServer.new(
|
28
|
+
configuration.server_host,
|
29
|
+
configuration.server_port
|
30
|
+
)
|
31
|
+
|
32
|
+
@destination = configuration.destination
|
33
|
+
end
|
34
|
+
|
35
|
+
##
|
36
|
+
# Run the server and accept all connection
|
37
|
+
#
|
38
|
+
# @return [nil]
|
39
|
+
#
|
40
|
+
def run
|
41
|
+
while (session = server.accept)
|
42
|
+
request = session.gets
|
43
|
+
logger.info(request)
|
44
|
+
|
45
|
+
trimmedrequest = request.gsub(/GET\ \//, '').gsub(/\ HTTP.*/, '').chomp
|
46
|
+
resource = "#{@destination}/#{trimmedrequest}"
|
47
|
+
|
48
|
+
# Try to download gem if file doesn't exists
|
49
|
+
fetch_gem(resource) unless File.exist?(resource)
|
50
|
+
|
51
|
+
# If not found again, return a 404
|
52
|
+
unless File.exist?(resource)
|
53
|
+
logger.warn("404 - #{trimmedrequest.gsub(/^public\//, '')}")
|
54
|
+
session.print "HTTP/1.1 404/Object Not Found\r\n\r\n"
|
55
|
+
session.close
|
56
|
+
next
|
57
|
+
end
|
58
|
+
|
59
|
+
if File.directory?(resource)
|
60
|
+
display_directory(session, resource)
|
61
|
+
else
|
62
|
+
mime_type = MIME::Types.type_for(resource)
|
63
|
+
session.print "HTTP/1.1 200/OK\r\nContent-type:#{mime_type}\r\n\r\n"
|
64
|
+
file = open(resource, 'rb')
|
65
|
+
session.puts(file.read)
|
66
|
+
end
|
67
|
+
|
68
|
+
session.close
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
##
|
73
|
+
# Try to fetch gem and download its if it's possible, and
|
74
|
+
# build and install indicies.
|
75
|
+
#
|
76
|
+
# @param [String] resource
|
77
|
+
# @return [Indexer]
|
78
|
+
#
|
79
|
+
def fetch_gem(resource)
|
80
|
+
name = File.basename(resource)
|
81
|
+
regexp = /^(.*)-(\d+(?:\.\d+){,4})\.gem(?:spec\.rz)?$/
|
82
|
+
gem_name, gem_version = name.match(regexp).captures
|
83
|
+
|
84
|
+
return unless gem_name && gem_version
|
85
|
+
|
86
|
+
logger.info("Try to download #{gem_name} with version #{gem_version}")
|
87
|
+
begin
|
88
|
+
gems_fetcher.source.gems.clear
|
89
|
+
gems_fetcher.source.gems.push(Gemirro::Gem.new(gem_name, gem_version))
|
90
|
+
gems_fetcher.fetch
|
91
|
+
rescue StandardError => e
|
92
|
+
logger.error(e.message)
|
93
|
+
end
|
94
|
+
|
95
|
+
generate_index
|
96
|
+
end
|
97
|
+
|
98
|
+
##
|
99
|
+
# Generate index and install indicies.
|
100
|
+
#
|
101
|
+
# @return [Indexer]
|
102
|
+
#
|
103
|
+
def generate_index
|
104
|
+
indexer = Indexer.new(configuration.destination)
|
105
|
+
indexer.ui = ::Gem::SilentUI.new
|
106
|
+
|
107
|
+
logger.info('Generating indexes')
|
108
|
+
indexer.generate_index
|
109
|
+
end
|
110
|
+
|
111
|
+
##
|
112
|
+
# Display directory on the current sesion
|
113
|
+
#
|
114
|
+
# @param [TCPSocket] session
|
115
|
+
# @param [String] resource
|
116
|
+
# @return [Array]
|
117
|
+
#
|
118
|
+
def display_directory(session, resource)
|
119
|
+
session.print "HTTP/1.1 200/OK\r\nContent-type:text/html\r\n\r\n"
|
120
|
+
base_dir = Dir.new(resource)
|
121
|
+
base_dir.entries.sort.each do |f|
|
122
|
+
dir_sign = ''
|
123
|
+
resource_path = resource.gsub(/\/$/, '') + '/' + f
|
124
|
+
dir_sign = '/' if File.directory?(resource_path)
|
125
|
+
resource_path = resource_path.gsub(/^public\//, '')
|
126
|
+
resource_path = resource_path.gsub(@destination, '')
|
127
|
+
|
128
|
+
session.print(
|
129
|
+
"<a href=\"#{resource_path}\">#{f}#{dir_sign}</a><br>"
|
130
|
+
) unless ['.', '..'].include?(File.basename(resource_path))
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
##
|
135
|
+
# @see Gemirro::Configuration#logger
|
136
|
+
# @return [Logger]
|
137
|
+
#
|
138
|
+
def logger
|
139
|
+
configuration.logger
|
140
|
+
end
|
141
|
+
|
142
|
+
##
|
143
|
+
# @see Gemirro.configuration
|
144
|
+
#
|
145
|
+
def configuration
|
146
|
+
Gemirro.configuration
|
147
|
+
end
|
148
|
+
|
149
|
+
##
|
150
|
+
# @see Gemirro::VersionsFetcher.fetch
|
151
|
+
#
|
152
|
+
def versions_fetcher
|
153
|
+
@versions_fetcher ||= Gemirro::VersionsFetcher.new(configuration.source).fetch
|
154
|
+
end
|
155
|
+
|
156
|
+
##
|
157
|
+
# @return [Gemirro::GemsFetcher]
|
158
|
+
#
|
159
|
+
def gems_fetcher
|
160
|
+
@gems_fetcher = Gemirro::GemsFetcher.new(
|
161
|
+
configuration.source, versions_fetcher)
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end
|