gemirro 1.5.0 → 2.0.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.
Files changed (47) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ruby.yml +29 -0
  3. data/.gitignore +1 -0
  4. data/.rubocop.yml +6 -3
  5. data/Gemfile.lock +126 -0
  6. data/MANIFEST +3 -10
  7. data/README.md +1 -1
  8. data/bin/gemirro +7 -1
  9. data/gemirro.gemspec +7 -2
  10. data/lib/gemirro/cli/index.rb +14 -4
  11. data/lib/gemirro/cli/init.rb +1 -1
  12. data/lib/gemirro/cli/update.rb +6 -0
  13. data/lib/gemirro/cli.rb +3 -1
  14. data/lib/gemirro/configuration.rb +4 -12
  15. data/lib/gemirro/gems_fetcher.rb +7 -10
  16. data/lib/gemirro/indexer.rb +286 -178
  17. data/lib/gemirro/mirror_file.rb +1 -0
  18. data/lib/gemirro/server.rb +56 -181
  19. data/lib/gemirro/source.rb +2 -15
  20. data/lib/gemirro/utils.rb +130 -65
  21. data/lib/gemirro/version.rb +1 -1
  22. data/lib/gemirro/versions_fetcher.rb +9 -10
  23. data/lib/gemirro/versions_file.rb +26 -24
  24. data/lib/gemirro.rb +0 -1
  25. data/spec/gemirro/configuration_spec.rb +9 -2
  26. data/spec/gemirro/gems_fetcher_spec.rb +1 -1
  27. data/spec/gemirro/indexer_spec.rb +5 -5
  28. data/spec/gemirro/server_spec.rb +79 -73
  29. data/spec/gemirro/source_spec.rb +1 -10
  30. data/spec/gemirro/versions_fetcher_spec.rb +3 -4
  31. data/spec/gemirro/versions_file_spec.rb +8 -32
  32. data/template/config.rb +7 -1
  33. data/template/public/dist/css/gemirro.css +25 -1
  34. data/views/gem.erb +46 -37
  35. data/views/index.erb +41 -33
  36. data/views/layout.erb +4 -17
  37. data/views/not_found.erb +4 -4
  38. metadata +79 -16
  39. data/lib/gemirro/cache.rb +0 -115
  40. data/spec/gemirro/cache_spec.rb +0 -32
  41. data/template/public/dist/css/bootstrap.min.css +0 -7
  42. data/template/public/dist/fonts/glyphicons-halflings-regular.eot +0 -0
  43. data/template/public/dist/fonts/glyphicons-halflings-regular.svg +0 -288
  44. data/template/public/dist/fonts/glyphicons-halflings-regular.ttf +0 -0
  45. data/template/public/dist/fonts/glyphicons-halflings-regular.woff +0 -0
  46. data/template/public/dist/fonts/glyphicons-halflings-regular.woff2 +0 -0
  47. data/template/public/dist/js/bootstrap.min.js +0 -7
@@ -4,18 +4,13 @@ require 'sinatra/base'
4
4
  require 'thin'
5
5
  require 'uri'
6
6
  require 'addressable/uri'
7
+ require 'base64'
7
8
 
8
9
  module Gemirro
9
10
  ##
10
11
  # Launch Sinatra server to easily download gems.
11
12
  #
12
13
  class Server < Sinatra::Base
13
- # rubocop:disable Layout/LineLength
14
- URI_REGEXP = /^(.*)-(\d+(?:\.\d+){1,4}.*?)(?:-(x86-(?:(?:mswin|mingw)(?:32|64)).*?|java))?\.(gem(?:spec\.rz)?)$/.freeze
15
- # rubocop:enable Layout/LineLength
16
- GEMSPEC_TYPE = 'gemspec.rz'
17
- GEM_TYPE = 'gem'
18
-
19
14
  access_logger = Logger.new(Utils.configuration.server.access_log).tap do |logger|
20
15
  ::Logger.class_eval { alias_method :write, :<< }
21
16
  logger.level = ::Logger::INFO
@@ -62,7 +57,7 @@ module Gemirro
62
57
  end
63
58
 
64
59
  ##
65
- # Display information about one gem
60
+ # Display information about one gem, human readable
66
61
  #
67
62
  # @return [nil]
68
63
  #
@@ -76,7 +71,7 @@ module Gemirro
76
71
 
77
72
  ##
78
73
  # Display home page containing the list of gems already
79
- # downloaded on the server
74
+ # downloaded on the server, human readable
80
75
  #
81
76
  # @return [nil]
82
77
  #
@@ -85,212 +80,92 @@ module Gemirro
85
80
  end
86
81
 
87
82
  ##
88
- # Return gem dependencies as binary
83
+ # compact_index, Return list of available gem names
89
84
  #
90
85
  # @return [nil]
91
86
  #
92
- get '/api/v1/dependencies' do
93
- content_type 'application/octet-stream'
94
- query_gems.any? ? Marshal.dump(query_gems_list) : 200
95
- end
87
+ get '/names' do
88
+ content_type 'text/plain'
96
89
 
97
- ##
98
- # Return gem dependencies as json
99
- #
100
- # @return [nil]
101
- #
102
- get '/api/v1/dependencies.json' do
103
- content_type 'application/json'
104
- query_gems.any? ? JSON.dump(query_gems_list) : {}
90
+ content_path = Dir.glob(File.join(Gemirro.configuration.destination, 'names.*.*.list')).last
91
+ _, etag, repr_digest, _ = File.basename(content_path).split('.')
92
+
93
+ headers 'etag' => %("#{etag}")
94
+ headers 'repr-digest' => %(sha-256=#{Base64.strict_encode64([repr_digest].pack('H*'))})
95
+ send_file content_path
105
96
  end
106
97
 
107
98
  ##
108
- # Try to get all request and download files
109
- # if files aren't found.
99
+ # compact_index, Return list of gem, including versions
110
100
  #
111
101
  # @return [nil]
112
102
  #
113
- get('*') do |path|
114
- resource = "#{settings.public_folder}#{path}"
103
+ get '/versions' do
104
+ content_type 'text/plain'
115
105
 
116
- # Try to download gem
117
- fetch_gem(resource) unless File.exist?(resource)
118
- # If not found again, return a 404
119
- return not_found unless File.exist?(resource)
106
+ content_path = Dir.glob(File.join(Utils.configuration.destination, 'versions.*.*.list')).last
107
+ _, etag, repr_digest, _ = File.basename(content_path).split('.')
120
108
 
121
- send_file(resource)
109
+ headers 'etag' => %("#{etag}")
110
+ headers 'repr-digest' => %(sha-256=#{Base64.strict_encode64([repr_digest].pack('H*'))})
111
+ send_file content_path
122
112
  end
123
113
 
124
- ##
125
- # Try to fetch gem and download its if it's possible, and
126
- # build and install indicies.
114
+ # compact_index, Return gem dependencies for all versions of a gem
127
115
  #
128
- # @param [String] resource
129
- # @return [Indexer]
116
+ # @return [nil]
130
117
  #
131
- def fetch_gem(resource)
132
- return unless Utils.configuration.fetch_gem
133
-
134
- name = File.basename(resource)
135
- result = name.match(URI_REGEXP)
136
- return unless result
137
-
138
- gem_name, gem_version, gem_platform, gem_type = result.captures
139
- return unless gem_name && gem_version
140
-
141
- begin
142
- gem = Utils.stored_gem(gem_name, gem_version, gem_platform)
143
- gem.gemspec = true if gem_type == GEMSPEC_TYPE
144
-
145
- return if Utils.gems_fetcher.gem_exists?(gem.filename(gem_version)) && gem_type == GEM_TYPE
146
- return if Utils.gems_fetcher.gemspec_exists?(gem.gemspec_filename(gem_version)) && gem_type == GEMSPEC_TYPE
147
-
148
- Utils.logger
149
- .info("Try to download #{gem_name} with version #{gem_version}")
150
- Utils.gems_fetcher.source.gems.clear
151
- Utils.gems_fetcher.source.gems.push(gem)
152
- Utils.gems_fetcher.fetch
118
+ get('/info/:gemname') do
119
+ gems = Utils.gems_collection
120
+ gem = gems.find_by_name(params[:gemname])
121
+ return not_found if gem.nil?
153
122
 
154
- update_indexes if Utils.configuration.update_on_fetch
155
- rescue StandardError => e
156
- Utils.logger.error(e)
157
- end
158
- end
123
+ content_type 'text/plain'
159
124
 
160
- ##
161
- # Update indexes files
162
- #
163
- # @return [Indexer]
164
- #
165
- def update_indexes
166
- indexer = Gemirro::Indexer.new(Utils.configuration.destination)
167
- indexer.only_origin = true
168
- indexer.ui = ::Gem::SilentUI.new
125
+ content_path = Dir.glob(File.join(Utils.configuration.destination, 'info', "#{params[:gemname]}.*.*.list")).last
126
+ _, etag, repr_digest, _ = File.basename(content_path).split('.')
169
127
 
170
- Utils.logger.info('Generating indexes')
171
- indexer.update_index
172
- indexer.updated_gems.each do |gem|
173
- Utils.cache.flush_key(File.basename(gem))
174
- end
175
- rescue SystemExit => e
176
- Utils.logger.info(e.message)
128
+ headers 'etag' => %("#{etag}")
129
+ headers 'repr-digest' => %(sha-256=#{Base64.strict_encode64([repr_digest].pack('H*'))})
130
+ send_file content_path
177
131
  end
178
132
 
179
133
  ##
180
- # Return all gems pass to query
134
+ # Try to get all request and download files
135
+ # if files aren't found.
181
136
  #
182
- # @return [Array]
137
+ # @return [nil]
183
138
  #
184
- def query_gems
185
- params[:gems].to_s.split(',')
186
- end
139
+ get('*') do |path|
140
+ resource = "#{settings.public_folder}#{path}"
187
141
 
188
- ##
189
- # Return gems list from query params
190
- #
191
- # @return [Array]
192
- #
193
- def query_gems_list
194
- Utils.gems_collection(false) # load collection
195
- gems = Parallel.map(query_gems, in_threads: 4) do |query_gem|
196
- gem_dependencies(query_gem)
197
- end
142
+ # Try to download gem
143
+ Gemirro::Utils.fetch_gem(resource) unless File.exist?(resource)
144
+ # If not found again, return a 404
145
+ return not_found unless File.exist?(resource)
198
146
 
199
- gems.flatten!
200
- gems.reject!(&:empty?)
201
- gems
147
+ send_file(resource)
202
148
  end
203
149
 
204
150
  ##
205
- # List of versions and dependencies of each version
206
- # from a gem name.
151
+ # Compile fragments for /api/v1/dependencies
207
152
  #
208
- # @return [Array]
153
+ # @return [nil]
209
154
  #
210
- def gem_dependencies(gem_name)
211
- Utils.cache.cache(gem_name) do
212
- gems = Utils.gems_collection(false)
213
- gem_collection = gems.find_by_name(gem_name)
214
-
215
- return '' if gem_collection.nil?
216
-
217
- gem_collection = Parallel.map(gem_collection, in_threads: 4) do |gem|
218
- [gem, spec_for(gem.name, gem.number, gem.platform)]
219
- end
220
- gem_collection.compact!
221
-
222
- Parallel.map(gem_collection, in_threads: 4) do |gem, spec|
223
- next if spec.nil?
224
-
225
- dependencies = spec.dependencies.select do |d|
226
- d.type == :runtime
227
- end
228
-
229
- dependencies = Parallel.map(dependencies, in_threads: 4) do |d|
230
- [d.name.is_a?(Array) ? d.name.first : d.name, d.requirement.to_s]
231
- end
232
-
233
- {
234
- name: gem.name,
235
- number: gem.number,
236
- platform: gem.platform,
237
- dependencies: dependencies
238
- }
239
- end
240
- end
241
- end
242
-
243
- helpers do
244
- ##
245
- # Return gem specification from gemname and version
246
- #
247
- # @param [String] gemname
248
- # @param [String] version
249
- # @return [::Gem::Specification]
250
- #
251
- def spec_for(gemname, version, platform = 'ruby')
252
- gem = Utils.stored_gem(gemname, version.to_s, platform)
253
- gemspec_path = File.join('quick',
254
- Gemirro::Configuration.marshal_identifier,
255
- gem.gemspec_filename)
256
- spec_file = File.join(settings.public_folder,
257
- gemspec_path)
258
- fetch_gem(gemspec_path) unless File.exist?(spec_file)
259
-
260
- return unless File.exist?(spec_file)
261
-
262
- File.open(spec_file, 'r') do |uz_file|
263
- uz_file.binmode
264
- inflater = Zlib::Inflate.new
265
- begin
266
- inflate_data = inflater.inflate(uz_file.read)
267
- ensure
268
- inflater.finish
269
- inflater.close
270
- end
271
- Marshal.load(inflate_data)
155
+ def dependencies_loader(names)
156
+ names.collect do |name|
157
+ f = File.join(settings.public_folder, 'api', 'v1', 'dependencies', "#{name}.*.*.list")
158
+ Marshal.load(File.read(Dir.glob(f).last))
159
+ rescue StandardError => e
160
+ env['rack.errors'].write "Cound not open #{f}\n"
161
+ env['rack.errors'].write "#{e.message}\n"
162
+ e.backtrace.each do |err|
163
+ env['rack.errors'].write "#{err}\n"
272
164
  end
165
+ nil
273
166
  end
274
-
275
- ##
276
- # Escape string
277
- #
278
- # @param [String] string
279
- # @return [String]
280
- #
281
- def escape(string)
282
- Rack::Utils.escape_html(string)
283
- end
284
-
285
- ##
286
- # Homepage link
287
- #
288
- # @param [Gem] spec
289
- # @return [String]
290
- #
291
- def homepage(spec)
292
- URI.parse(Addressable::URI.escape(spec.homepage))
293
- end
167
+ .flatten
168
+ .compact
294
169
  end
295
170
  end
296
171
  end
@@ -33,23 +33,10 @@ module Gemirro
33
33
  #
34
34
  def fetch_versions
35
35
  Utils.logger.info(
36
- "Fetching #{Configuration.versions_file} on #{@name} (#{@host})"
36
+ "Fetching versions on #{@name} (#{@host})"
37
37
  )
38
38
 
39
- Http.get("#{host}/#{Configuration.versions_file}").body
40
- end
41
-
42
- ##
43
- # Fetches a list of all the available Gems and their versions.
44
- #
45
- # @return [String]
46
- #
47
- def fetch_prerelease_versions
48
- Utils.logger.info(
49
- "Fetching #{Configuration.prerelease_versions_file} " \
50
- "on #{@name} (#{@host})"
51
- )
52
- Http.get("#{host}/#{Configuration.prerelease_versions_file}").body
39
+ Http.get("#{host}/versions").body
53
40
  end
54
41
 
55
42
  ##
data/lib/gemirro/utils.rb CHANGED
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'gemirro/gem_version'
4
+
3
5
  module Gemirro
4
6
  ##
5
7
  # The Utils class is responsible for executing specific traitments
@@ -13,84 +15,60 @@ module Gemirro
13
15
  # @return [Gemirro::GemsFetcher]
14
16
  #
15
17
  class Utils
16
- attr_reader(:cache,
17
- :versions_fetcher,
18
- :gems_fetcher,
19
- :gems_collection,
20
- :stored_gems)
18
+ attr_reader(
19
+ :versions_fetcher,
20
+ :gems_fetcher,
21
+ :gems_collection,
22
+ :stored_gems
23
+ )
21
24
 
22
- ##
23
- # Cache class to store marshal and data into files
24
- #
25
- # @return [Gemirro::Cache]
26
- #
27
- def self.cache
28
- @cache ||= Gemirro::Cache
29
- .new(File.join(configuration.destination, '.cache'))
30
- end
25
+ URI_REGEXP = /^(.*)-(\d+(?:\.\d+){1,4}.*?)(?:-(x86-(?:(?:mswin|mingw)(?:32|64)).*?|java))?\.(gem(?:spec\.rz)?)$/
26
+ GEMSPEC_TYPE = 'gemspec.rz'
27
+ GEM_TYPE = 'gem'
31
28
 
32
29
  ##
33
- # Generate Gems collection from Marshal dump
30
+ # Generate Gems collection from Marshal dump - always the .local file
34
31
  #
35
- # @param [TrueClass|FalseClass] orig Fetch orig files
36
32
  # @return [Gemirro::GemVersionCollection]
37
33
  #
38
- def self.gems_collection(orig = true)
39
- @gems_collection = {} if @gems_collection.nil?
34
+ def self.gems_collection
35
+ @gems_collection ||= { files: {}, values: nil }
40
36
 
41
- is_orig = orig ? 1 : 0
42
- data = @gems_collection[is_orig]
43
- data = { files: {}, values: nil } if data.nil?
37
+ file_paths =
38
+ Dir.glob(File.join(
39
+ Gemirro.configuration.destination,
40
+ 'versions.*.*.list'
41
+ ))
44
42
 
45
- file_paths = specs_files_paths(orig)
46
- has_file_changed = false
47
- Parallel.map(file_paths, in_threads: 4) do |file_path|
48
- next if data[:files].key?(file_path) &&
49
- data[:files][file_path] == File.mtime(file_path)
50
-
51
- has_file_changed = true
52
- end
43
+ has_file_changed =
44
+ @gems_collection[:files] != file_paths.each_with_object({}) do |f, r|
45
+ r[f] = File.mtime(f) if File.exist?(f)
46
+ end
53
47
 
54
48
  # Return result if no file changed
55
- return data[:values] if !has_file_changed && !data[:values].nil?
49
+ return @gems_collection[:values] if !has_file_changed && !@gems_collection[:values].nil?
56
50
 
57
51
  gems = []
58
- Parallel.map(file_paths, in_threads: 4) do |file_path|
59
- next unless File.exist?(file_path)
60
52
 
61
- gems.concat(Marshal.load(Zlib::GzipReader.open(file_path).read))
62
- data[:files][file_path] = File.mtime(file_path)
63
- end
53
+ CompactIndex::VersionsFile.new(file_paths.last).contents.each_line.with_index do |line, index|
54
+ next if index < 2
64
55
 
65
- collection = GemVersionCollection.new(gems)
66
- data[:values] = collection
56
+ gem_name = line.split[0]
57
+ versions = line.split[1..-2].collect { |x| x.split(',') }.flatten # All except first and last
67
58
 
68
- collection
69
- end
59
+ versions.each do |ver|
60
+ version, platform =
61
+ if ver.include?('-')
62
+ ver.split('-', 2)
63
+ else
64
+ [ver, 'ruby']
65
+ end
70
66
 
71
- ##
72
- # Return specs fils paths
73
- #
74
- # @param [TrueClass|FalseClass] orig Fetch orig files
75
- # @return [Array]
76
- #
77
- def self.specs_files_paths(orig = true)
78
- marshal_version = Gemirro::Configuration.marshal_version
79
- Parallel.map(specs_file_types, in_threads: 4) do |specs_file_type|
80
- File.join(configuration.destination,
81
- [specs_file_type,
82
- marshal_version,
83
- "gz#{orig ? '.orig' : ''}"].join('.'))
67
+ gems << Gemirro::GemVersion.new(gem_name, version, platform)
68
+ end
84
69
  end
85
- end
86
70
 
87
- ##
88
- # Return specs fils types
89
- #
90
- # @return [Array]
91
- #
92
- def self.specs_file_types
93
- %i[specs prerelease_specs]
71
+ @gems_collection[:values] = GemVersionCollection.new(gems)
94
72
  end
95
73
 
96
74
  ##
@@ -112,8 +90,7 @@ module Gemirro
112
90
  # @see Gemirro::VersionsFetcher.fetch
113
91
  #
114
92
  def self.versions_fetcher
115
- @versions_fetcher ||= Gemirro::VersionsFetcher
116
- .new(configuration.source).fetch
93
+ @versions_fetcher ||= Gemirro::VersionsFetcher.new(configuration.source).fetch
117
94
  end
118
95
 
119
96
  ##
@@ -133,13 +110,101 @@ module Gemirro
133
110
  def self.stored_gem(gem_name, gem_version, platform = 'ruby')
134
111
  platform = 'ruby' if platform.nil?
135
112
  @stored_gems ||= {}
136
- # rubocop:disable Metrics/LineLength
137
113
  @stored_gems[gem_name] = {} unless @stored_gems.key?(gem_name)
138
114
  @stored_gems[gem_name][gem_version] = {} unless @stored_gems[gem_name].key?(gem_version)
139
- @stored_gems[gem_name][gem_version][platform] ||= Gem.new(gem_name, gem_version, platform) unless @stored_gems[gem_name][gem_version].key?(platform)
140
- # rubocop:enable Metrics/LineLength
115
+ unless @stored_gems[gem_name][gem_version].key?(platform)
116
+ @stored_gems[gem_name][gem_version][platform] ||= Gem.new(gem_name, gem_version, platform)
117
+ end
141
118
 
142
119
  @stored_gems[gem_name][gem_version][platform]
143
120
  end
121
+
122
+ ##
123
+ # Return gem specification from gemname and version
124
+ #
125
+ # @param [String] gemname
126
+ # @param [String] version
127
+ # @return [::Gem::Specification]
128
+ #
129
+ def self.spec_for(gemname, version, platform)
130
+ gem = Utils.stored_gem(gemname, version.to_s, platform)
131
+
132
+ spec_file =
133
+ File.join(
134
+ configuration.destination,
135
+ 'quick',
136
+ Gemirro::Configuration.marshal_identifier,
137
+ gem.gemspec_filename
138
+ )
139
+
140
+ fetch_gem(spec_file) unless File.exist?(spec_file)
141
+
142
+ # this is a separate action
143
+ return unless File.exist?(spec_file)
144
+
145
+ File.open(spec_file, 'r') do |uz_file|
146
+ uz_file.binmode
147
+ inflater = Zlib::Inflate.new
148
+ begin
149
+ inflate_data = inflater.inflate(uz_file.read)
150
+ ensure
151
+ inflater.finish
152
+ inflater.close
153
+ end
154
+
155
+ Marshal.load(inflate_data)
156
+ end
157
+ end
158
+
159
+ ##
160
+ # Try to fetch gem and download its if it's possible, and
161
+ # build and install indicies.
162
+ #
163
+ # @param [String] resource
164
+ # @return [Indexer]
165
+ #
166
+ def self.fetch_gem(resource)
167
+ return unless Utils.configuration.fetch_gem
168
+
169
+ name = File.basename(resource)
170
+ result = name.match(URI_REGEXP)
171
+ return unless result
172
+
173
+ gem_name, gem_version, gem_platform, gem_type = result.captures
174
+ return unless gem_name && gem_version
175
+
176
+ begin
177
+ gem = Utils.stored_gem(gem_name, gem_version, gem_platform)
178
+ gem.gemspec = true if gem_type == GEMSPEC_TYPE
179
+
180
+ return if Utils.gems_fetcher.gem_exists?(gem.filename(gem_version)) && gem_type == GEM_TYPE
181
+ return if Utils.gems_fetcher.gemspec_exists?(gem.gemspec_filename(gem_version)) && gem_type == GEMSPEC_TYPE
182
+
183
+ Utils.logger.info("Try to download #{gem_name} with version #{gem_version}")
184
+ Utils.gems_fetcher.source.gems.clear
185
+ Utils.gems_fetcher.source.gems.push(gem)
186
+ Utils.gems_fetcher.fetch
187
+
188
+ update_indexes if Utils.configuration.update_on_fetch
189
+ rescue StandardError => e
190
+ Utils.logger.error(e)
191
+ end
192
+ end
193
+
194
+ ##
195
+ # Update indexes files
196
+ #
197
+ # @return [Indexer]
198
+ #
199
+ def self.update_indexes
200
+ indexer = Gemirro::Indexer.new
201
+ indexer.only_origin = true
202
+ indexer.ui = ::Gem::SilentUI.new
203
+
204
+ Utils.logger.info('Generating indexes')
205
+ indexer.update_index
206
+ rescue SystemExit => e
207
+ Utils.logger.info(e.message)
208
+ end
144
209
  end
145
210
  end
@@ -2,5 +2,5 @@
2
2
 
3
3
  # Gemirro Version
4
4
  module Gemirro
5
- VERSION = '1.5.0'
5
+ VERSION = '2.0.0'
6
6
  end
@@ -22,25 +22,24 @@ module Gemirro
22
22
  # @return [Gemirro::VersionsFile]
23
23
  #
24
24
  def fetch
25
- VersionsFile.load(read_file(Configuration.versions_file),
26
- read_file(Configuration.prerelease_versions_file, true))
25
+ return unless Gemirro.configuration.versions_file
26
+
27
+ VersionsFile.new(read_file(Gemirro.configuration.versions_file))
27
28
  end
28
29
 
29
30
  ##
30
31
  # Read file if exists otherwise download its from source
31
32
  #
32
33
  # @param [String] file name
33
- # @param [TrueClass|FalseClass] prerelease Is prerelease or not
34
34
  #
35
- def read_file(file, prerelease = false)
36
- destination = Gemirro.configuration.destination
37
- file_dst = File.join(destination, file)
38
- unless File.exist?(file_dst)
39
- File.write(file_dst, @source.fetch_versions) unless prerelease
40
- File.write(file_dst, @source.fetch_prerelease_versions) if prerelease
35
+ def read_file(file)
36
+ unless File.exist?(file)
37
+ throw 'No source defined' unless @source
38
+
39
+ File.write(file, @source.fetch_versions)
41
40
  end
42
41
 
43
- File.read(file_dst)
42
+ File.read(file)
44
43
  end
45
44
  end
46
45
  end
@@ -11,36 +11,24 @@ module Gemirro
11
11
  # @return [Hash]
12
12
  #
13
13
  class VersionsFile
14
- attr_reader :versions, :versions_hash
14
+ attr_reader :versions_string, :versions_hash
15
15
 
16
16
  ##
17
17
  # Reads the versions file from the specified String.
18
18
  #
19
- # @param [String] spec_content
20
- # @param [String] prerelease_content
19
+ # @param [String] versions_content
21
20
  # @return [Gemirro::VersionsFile]
22
21
  #
23
- def self.load(spec_content, prerelease_content)
24
- buffer = StringIO.new(spec_content)
25
- reader = Zlib::GzipReader.new(buffer)
26
- versions = Marshal.load(reader.read)
27
-
28
- buffer = StringIO.new(prerelease_content)
29
- reader = Zlib::GzipReader.new(buffer)
30
- versions.concat(Marshal.load(reader.read))
31
-
32
- instance = new(versions)
33
-
34
- reader.close
35
-
36
- instance
37
- end
38
22
 
39
23
  ##
40
- # @param [Array] versions
24
+ # @param [String] versions
41
25
  #
42
- def initialize(versions)
43
- @versions = versions
26
+ def initialize(versions_string)
27
+ unless versions_string.is_a? String
28
+ throw "#{versions_string.class} is wrong format, expect String; #{versions_string.inspect}"
29
+ end
30
+
31
+ @versions_string = versions_string
44
32
  @versions_hash = create_versions_hash
45
33
  end
46
34
 
@@ -53,10 +41,24 @@ module Gemirro
53
41
  def create_versions_hash
54
42
  hash = Hash.new { |h, k| h[k] = [] }
55
43
 
56
- versions.each do |version|
57
- hash[version[0]] << version
58
- end
44
+ versions_string.each_line.with_index do |line, index|
45
+ next if index < 2
59
46
 
47
+ parts = line.split
48
+ gem_name = parts[0]
49
+ parts[-1]
50
+ versions = parts[1..-2].collect { |x| x.split(',') }.flatten # All except first and last
51
+
52
+ versions.each do |ver|
53
+ version, platform =
54
+ if ver.include?('-')
55
+ ver.split('-', 2)
56
+ else
57
+ [ver, 'ruby']
58
+ end
59
+ hash[gem_name] << [gem_name, ::Gem::Version.new(version), platform]
60
+ end
61
+ end
60
62
  hash
61
63
  end
62
64