suvii 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +14 -0
- data/.travis.yml +10 -0
- data/.yardopts +1 -0
- data/CHANGELOG.md +1 -0
- data/CONTRIBUTING.md +47 -0
- data/Gemfile +6 -0
- data/LICENSE.txt +22 -0
- data/README.md +51 -0
- data/Rakefile +7 -0
- data/lib/suvii.rb +40 -0
- data/lib/suvii/cache.rb +24 -0
- data/lib/suvii/extract.rb +58 -0
- data/lib/suvii/extract/targz.rb +49 -0
- data/lib/suvii/extract/zip.rb +19 -0
- data/lib/suvii/http.rb +31 -0
- data/suvii.gemspec +23 -0
- data/test/acceptance_test.rb +29 -0
- data/test/cache_test.rb +42 -0
- data/test/extract/targz_test.rb +28 -0
- data/test/extract/zip_test.rb +28 -0
- data/test/extract_test.rb +26 -0
- data/test/fixtures/results/Trumbowyg-1.1.7/.gitattributes +6 -0
- data/test/fixtures/results/Trumbowyg-1.1.7/.jshintrc +15 -0
- data/test/fixtures/results/Trumbowyg-1.1.7/Gulpfile.js +167 -0
- data/test/fixtures/results/Trumbowyg-1.1.7/LICENSE +21 -0
- data/test/fixtures/results/Trumbowyg-1.1.7/README.md +29 -0
- data/test/fixtures/results/Trumbowyg-1.1.7/bower.json +36 -0
- data/test/fixtures/results/Trumbowyg-1.1.7/dist/langs/ca.min.js +11 -0
- data/test/fixtures/results/Trumbowyg-1.1.7/dist/langs/da.min.js +9 -0
- data/test/fixtures/results/Trumbowyg-1.1.7/dist/langs/de.min.js +9 -0
- data/test/fixtures/results/Trumbowyg-1.1.7/dist/langs/es.min.js +9 -0
- data/test/fixtures/results/Trumbowyg-1.1.7/dist/langs/es_ar.min.js +9 -0
- data/test/fixtures/results/Trumbowyg-1.1.7/dist/langs/fa.min.js +10 -0
- data/test/fixtures/results/Trumbowyg-1.1.7/dist/langs/fi.min.js +9 -0
- data/test/fixtures/results/Trumbowyg-1.1.7/dist/langs/fr.min.js +10 -0
- data/test/fixtures/results/Trumbowyg-1.1.7/dist/langs/he.min.js +9 -0
- data/test/fixtures/results/Trumbowyg-1.1.7/dist/langs/id.min.js +11 -0
- data/test/fixtures/results/Trumbowyg-1.1.7/dist/langs/it.min.js +8 -0
- data/test/fixtures/results/Trumbowyg-1.1.7/dist/langs/ko.min.js +10 -0
- data/test/fixtures/results/Trumbowyg-1.1.7/dist/langs/pl.min.js +9 -0
- data/test/fixtures/results/Trumbowyg-1.1.7/dist/langs/pt.min.js +11 -0
- data/test/fixtures/results/Trumbowyg-1.1.7/dist/langs/ro.min.js +13 -0
- data/test/fixtures/results/Trumbowyg-1.1.7/dist/langs/ru.min.js +8 -0
- data/test/fixtures/results/Trumbowyg-1.1.7/dist/langs/tr.min.js +10 -0
- data/test/fixtures/results/Trumbowyg-1.1.7/dist/langs/zh_cn.min.js +10 -0
- data/test/fixtures/results/Trumbowyg-1.1.7/dist/plugins/base64/trumbowyg.base64.js +74 -0
- data/test/fixtures/results/Trumbowyg-1.1.7/dist/plugins/base64/trumbowyg.base64.min.js +1 -0
- data/test/fixtures/results/Trumbowyg-1.1.7/dist/plugins/colors/trumbowyg.colors.js +69 -0
- data/test/fixtures/results/Trumbowyg-1.1.7/dist/plugins/colors/trumbowyg.colors.min.js +1 -0
- data/test/fixtures/results/Trumbowyg-1.1.7/dist/plugins/colors/ui/images/icons-2x.png +0 -0
- data/test/fixtures/results/Trumbowyg-1.1.7/dist/plugins/colors/ui/images/icons.png +0 -0
- data/test/fixtures/results/Trumbowyg-1.1.7/dist/plugins/colors/ui/trumbowyg.colors.css +55 -0
- data/test/fixtures/results/Trumbowyg-1.1.7/dist/plugins/colors/ui/trumbowyg.colors.min.css +2 -0
- data/test/fixtures/results/Trumbowyg-1.1.7/dist/plugins/upload/trumbowyg.upload.js +140 -0
- data/test/fixtures/results/Trumbowyg-1.1.7/dist/plugins/upload/trumbowyg.upload.min.js +1 -0
- data/test/fixtures/results/Trumbowyg-1.1.7/dist/trumbowyg.js +1124 -0
- data/test/fixtures/results/Trumbowyg-1.1.7/dist/trumbowyg.min.js +2 -0
- data/test/fixtures/results/Trumbowyg-1.1.7/dist/ui/images/icons-2x.png +0 -0
- data/test/fixtures/results/Trumbowyg-1.1.7/dist/ui/images/icons.png +0 -0
- data/test/fixtures/results/Trumbowyg-1.1.7/dist/ui/trumbowyg.css +471 -0
- data/test/fixtures/results/Trumbowyg-1.1.7/dist/ui/trumbowyg.min.css +2 -0
- data/test/fixtures/results/Trumbowyg-1.1.7/examples/css/main.css +14 -0
- data/test/fixtures/results/Trumbowyg-1.1.7/examples/index.html +41 -0
- data/test/fixtures/results/Trumbowyg-1.1.7/examples/plugins/colors.html +49 -0
- data/test/fixtures/results/Trumbowyg-1.1.7/index.html +252 -0
- data/test/fixtures/results/Trumbowyg-1.1.7/package.json +48 -0
- data/test/fixtures/results/Trumbowyg-1.1.7/plugins/base64/trumbowyg.base64.js +74 -0
- data/test/fixtures/results/Trumbowyg-1.1.7/plugins/colors/Gulpfile.js +101 -0
- data/test/fixtures/results/Trumbowyg-1.1.7/plugins/colors/trumbowyg.colors.js +69 -0
- data/test/fixtures/results/Trumbowyg-1.1.7/plugins/colors/ui/images/icons-2x/backcolor.png +0 -0
- data/test/fixtures/results/Trumbowyg-1.1.7/plugins/colors/ui/images/icons-2x/forecolor.png +0 -0
- data/test/fixtures/results/Trumbowyg-1.1.7/plugins/colors/ui/images/icons/backcolor.png +0 -0
- data/test/fixtures/results/Trumbowyg-1.1.7/plugins/colors/ui/images/icons/forecolor.png +0 -0
- data/test/fixtures/results/Trumbowyg-1.1.7/plugins/colors/ui/sass/trumbowyg.colors.scss +65 -0
- data/test/fixtures/results/Trumbowyg-1.1.7/plugins/upload/trumbowyg.upload.js +140 -0
- data/test/fixtures/results/Trumbowyg-1.1.7/plugins/upload/trumbowyg.upload.php +49 -0
- data/test/fixtures/results/Trumbowyg-1.1.7/src/langs/ca.js +58 -0
- data/test/fixtures/results/Trumbowyg-1.1.7/src/langs/da.js +56 -0
- data/test/fixtures/results/Trumbowyg-1.1.7/src/langs/de.js +56 -0
- data/test/fixtures/results/Trumbowyg-1.1.7/src/langs/en.js +14 -0
- data/test/fixtures/results/Trumbowyg-1.1.7/src/langs/es.js +56 -0
- data/test/fixtures/results/Trumbowyg-1.1.7/src/langs/es_ar.js +56 -0
- data/test/fixtures/results/Trumbowyg-1.1.7/src/langs/fa.js +57 -0
- data/test/fixtures/results/Trumbowyg-1.1.7/src/langs/fi.js +56 -0
- data/test/fixtures/results/Trumbowyg-1.1.7/src/langs/fr.js +57 -0
- data/test/fixtures/results/Trumbowyg-1.1.7/src/langs/he.js +58 -0
- data/test/fixtures/results/Trumbowyg-1.1.7/src/langs/id.js +58 -0
- data/test/fixtures/results/Trumbowyg-1.1.7/src/langs/it.js +55 -0
- data/test/fixtures/results/Trumbowyg-1.1.7/src/langs/ko.js +57 -0
- data/test/fixtures/results/Trumbowyg-1.1.7/src/langs/pl.js +56 -0
- data/test/fixtures/results/Trumbowyg-1.1.7/src/langs/pt.js +58 -0
- data/test/fixtures/results/Trumbowyg-1.1.7/src/langs/ro.js +60 -0
- data/test/fixtures/results/Trumbowyg-1.1.7/src/langs/ru.js +55 -0
- data/test/fixtures/results/Trumbowyg-1.1.7/src/langs/tr.js +57 -0
- data/test/fixtures/results/Trumbowyg-1.1.7/src/langs/zh_cn.js +57 -0
- data/test/fixtures/results/Trumbowyg-1.1.7/src/trumbowyg.js +1113 -0
- data/test/fixtures/results/Trumbowyg-1.1.7/src/ui/images/icons-2x/attachement.png +0 -0
- data/test/fixtures/results/Trumbowyg-1.1.7/src/ui/images/icons-2x/barre.png +0 -0
- data/test/fixtures/results/Trumbowyg-1.1.7/src/ui/images/icons-2x/bold.png +0 -0
- data/test/fixtures/results/Trumbowyg-1.1.7/src/ui/images/icons-2x/center-align.png +0 -0
- data/test/fixtures/results/Trumbowyg-1.1.7/src/ui/images/icons-2x/close.png +0 -0
- data/test/fixtures/results/Trumbowyg-1.1.7/src/ui/images/icons-2x/formatting.png +0 -0
- data/test/fixtures/results/Trumbowyg-1.1.7/src/ui/images/icons-2x/fullscreen-exit.png +0 -0
- data/test/fixtures/results/Trumbowyg-1.1.7/src/ui/images/icons-2x/fullscreen.png +0 -0
- data/test/fixtures/results/Trumbowyg-1.1.7/src/ui/images/icons-2x/gras.png +0 -0
- data/test/fixtures/results/Trumbowyg-1.1.7/src/ui/images/icons-2x/horizontal-rule.png +0 -0
- data/test/fixtures/results/Trumbowyg-1.1.7/src/ui/images/icons-2x/image.png +0 -0
- data/test/fixtures/results/Trumbowyg-1.1.7/src/ui/images/icons-2x/italic.png +0 -0
- data/test/fixtures/results/Trumbowyg-1.1.7/src/ui/images/icons-2x/justify-align.png +0 -0
- data/test/fixtures/results/Trumbowyg-1.1.7/src/ui/images/icons-2x/left-align.png +0 -0
- data/test/fixtures/results/Trumbowyg-1.1.7/src/ui/images/icons-2x/link.png +0 -0
- data/test/fixtures/results/Trumbowyg-1.1.7/src/ui/images/icons-2x/ordered-list.png +0 -0
- data/test/fixtures/results/Trumbowyg-1.1.7/src/ui/images/icons-2x/right-align.png +0 -0
- data/test/fixtures/results/Trumbowyg-1.1.7/src/ui/images/icons-2x/souligne.png +0 -0
- data/test/fixtures/results/Trumbowyg-1.1.7/src/ui/images/icons-2x/strikethrough.png +0 -0
- data/test/fixtures/results/Trumbowyg-1.1.7/src/ui/images/icons-2x/underline.png +0 -0
- data/test/fixtures/results/Trumbowyg-1.1.7/src/ui/images/icons-2x/unordered-list.png +0 -0
- data/test/fixtures/results/Trumbowyg-1.1.7/src/ui/images/icons-2x/video.png +0 -0
- data/test/fixtures/results/Trumbowyg-1.1.7/src/ui/images/icons-2x/view-html.png +0 -0
- data/test/fixtures/results/Trumbowyg-1.1.7/src/ui/images/icons/attachement.png +0 -0
- data/test/fixtures/results/Trumbowyg-1.1.7/src/ui/images/icons/barre.png +0 -0
- data/test/fixtures/results/Trumbowyg-1.1.7/src/ui/images/icons/bold.png +0 -0
- data/test/fixtures/results/Trumbowyg-1.1.7/src/ui/images/icons/center-align.png +0 -0
- data/test/fixtures/results/Trumbowyg-1.1.7/src/ui/images/icons/close.png +0 -0
- data/test/fixtures/results/Trumbowyg-1.1.7/src/ui/images/icons/formatting.png +0 -0
- data/test/fixtures/results/Trumbowyg-1.1.7/src/ui/images/icons/fullscreen-exit.png +0 -0
- data/test/fixtures/results/Trumbowyg-1.1.7/src/ui/images/icons/fullscreen.png +0 -0
- data/test/fixtures/results/Trumbowyg-1.1.7/src/ui/images/icons/gras.png +0 -0
- data/test/fixtures/results/Trumbowyg-1.1.7/src/ui/images/icons/horizontal-rule.png +0 -0
- data/test/fixtures/results/Trumbowyg-1.1.7/src/ui/images/icons/image.png +0 -0
- data/test/fixtures/results/Trumbowyg-1.1.7/src/ui/images/icons/italic.png +0 -0
- data/test/fixtures/results/Trumbowyg-1.1.7/src/ui/images/icons/justify-align.png +0 -0
- data/test/fixtures/results/Trumbowyg-1.1.7/src/ui/images/icons/left-align.png +0 -0
- data/test/fixtures/results/Trumbowyg-1.1.7/src/ui/images/icons/link.png +0 -0
- data/test/fixtures/results/Trumbowyg-1.1.7/src/ui/images/icons/ordered-list.png +0 -0
- data/test/fixtures/results/Trumbowyg-1.1.7/src/ui/images/icons/right-align.png +0 -0
- data/test/fixtures/results/Trumbowyg-1.1.7/src/ui/images/icons/souligne.png +0 -0
- data/test/fixtures/results/Trumbowyg-1.1.7/src/ui/images/icons/strikethrough.png +0 -0
- data/test/fixtures/results/Trumbowyg-1.1.7/src/ui/images/icons/underline.png +0 -0
- data/test/fixtures/results/Trumbowyg-1.1.7/src/ui/images/icons/unordered-list.png +0 -0
- data/test/fixtures/results/Trumbowyg-1.1.7/src/ui/images/icons/video.png +0 -0
- data/test/fixtures/results/Trumbowyg-1.1.7/src/ui/images/icons/view-html.png +0 -0
- data/test/fixtures/results/Trumbowyg-1.1.7/src/ui/sass/mixins/_sprite-pos.scss +7 -0
- data/test/fixtures/results/Trumbowyg-1.1.7/src/ui/sass/trumbowyg.scss +564 -0
- data/test/fixtures/trumbowyg.tar.gz +0 -0
- data/test/fixtures/trumbowyg.zip +0 -0
- data/test/http_test.rb +61 -0
- data/test/test_helper.rb +24 -0
- metadata +409 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 5bf394d2a7c3da8466bf22f4d738f02034174fa7
|
4
|
+
data.tar.gz: 7664c6d478ba6b2f97de9961eb3d68a5b610d468
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: ecdf92f1be591c5e8b934376c0b3825747657fb58de474cad4e6250f3c3bb101ba2a80fa1f65cb2ac61b78d30042abd6d0b8cfcc11610f0d08053261ba5cbe3f
|
7
|
+
data.tar.gz: bba9d298fd9513fdcb5502b14abbdcc9284c0cb286a0bba201f471a359d1f4a66ec6d5ab13ef85f3c2bda748f0b4b3990407a44c266e3f9b3f0ab0ed8ee8af54
|
data/.gitignore
ADDED
data/.travis.yml
ADDED
data/.yardopts
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--no-private
|
data/CHANGELOG.md
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
## Unreleased
|
data/CONTRIBUTING.md
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
# Contributing
|
2
|
+
|
3
|
+
## Adding a feature
|
4
|
+
|
5
|
+
1. Open an issue and explain what you're planning to do. It is better to discuss new idea first,
|
6
|
+
rather when diving into code.
|
7
|
+
2. Add some tests.
|
8
|
+
3. Write the code.
|
9
|
+
4. Make sure all tests pass.
|
10
|
+
5. Commit with detailed explanation what you've done in a message.
|
11
|
+
6. Open pull request.
|
12
|
+
|
13
|
+
## Breaking/removing a feature
|
14
|
+
|
15
|
+
1. Add deprecation warning and fallback to old behaivour if possible.
|
16
|
+
2. Explain how to migrate to the new code in CHANGELOG.
|
17
|
+
3. Update/remove tests.
|
18
|
+
4. Update the code.
|
19
|
+
5. Make sure all tests pass.
|
20
|
+
6. Commit with detailed explanation what you've done in a message.
|
21
|
+
7. Open pull request.
|
22
|
+
|
23
|
+
## Fixing a bug
|
24
|
+
|
25
|
+
1. Add failing test.
|
26
|
+
2. Fix the bug.
|
27
|
+
3. Make sure all tests pass.
|
28
|
+
4. Commit with detailed explanation what you've done in a message.
|
29
|
+
5. Open pull request.
|
30
|
+
|
31
|
+
## Fixing a typo
|
32
|
+
|
33
|
+
1. Commit with a message that include "[ci skip]" remark.
|
34
|
+
2. Open pull request.
|
35
|
+
|
36
|
+
## Running the tests
|
37
|
+
|
38
|
+
```
|
39
|
+
rake test
|
40
|
+
```
|
41
|
+
|
42
|
+
## Working with documentation
|
43
|
+
|
44
|
+
```
|
45
|
+
yard server -dr
|
46
|
+
open http://localhost:8808
|
47
|
+
```
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2016 Andrii Malyshko
|
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,51 @@
|
|
1
|
+
# Suvii
|
2
|
+
|
3
|
+
[![Build Status](https://img.shields.io/travis/torba-rb/suvii.svg)](https://travis-ci.org/torba-rb/suvii)
|
4
|
+
[![Gem version](https://img.shields.io/gem/v/suvii.svg)](https://rubygems.org/gems/suvii)
|
5
|
+
|
6
|
+
**Suvii** is a library that abstracts content reading from a remote tar.gz/zip archive. It fetches
|
7
|
+
the archive and extracts it to a temp directory.
|
8
|
+
|
9
|
+
## Name origin
|
10
|
+
|
11
|
+
"Сувій" [[suʋii̯][suvii-pronounce]] in Ukrainian can mean a scroll, something which is rolled up or
|
12
|
+
even a package.
|
13
|
+
|
14
|
+
## Status
|
15
|
+
|
16
|
+
Production ready.
|
17
|
+
|
18
|
+
## Documentation
|
19
|
+
|
20
|
+
[Released version](http://rubydoc.info/gems/suvii/0.1.0)
|
21
|
+
|
22
|
+
|
23
|
+
## Installation
|
24
|
+
|
25
|
+
Add this line to your application's Gemfile and run `bundle`:
|
26
|
+
|
27
|
+
```ruby
|
28
|
+
gem 'suvii'
|
29
|
+
```
|
30
|
+
|
31
|
+
## Usage
|
32
|
+
|
33
|
+
```ruby
|
34
|
+
temp_directory = Suvii.fetch("https://registry.npmjs.org/coffee-script/-/coffee-script-2.0.0.tgz")
|
35
|
+
# do whatever you need with extracted content
|
36
|
+
Dir.glob("#{temp_directory}/**/*").each do |entry|
|
37
|
+
# ...
|
38
|
+
end
|
39
|
+
```
|
40
|
+
|
41
|
+
Note, that fetching same URL twice will unpack to another temp directory. Such design decision was
|
42
|
+
based on assumption, that generally you don't need everything in an archive, or even have to modify
|
43
|
+
something. This extra copy/modify step should be totally independent from extraction, and you shouldn't
|
44
|
+
worry about cleanup, or that another process could mess things up.
|
45
|
+
|
46
|
+
## Origin
|
47
|
+
|
48
|
+
Extracted from [Torba][torba-github] library since it looks more like a standalone component.
|
49
|
+
|
50
|
+
[suvii-pronounce]: https://commons.wikimedia.org/wiki/File:Uk-%D1%81%D1%83%D0%B2%D1%96%D0%B9.ogg
|
51
|
+
[torba-github]: https://github.com/torba-rb/torba
|
data/Rakefile
ADDED
data/lib/suvii.rb
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
require "tmpdir"
|
2
|
+
require "logger"
|
3
|
+
|
4
|
+
require "suvii/cache"
|
5
|
+
require "suvii/http"
|
6
|
+
require "suvii/extract"
|
7
|
+
|
8
|
+
# @since 0.1.0
|
9
|
+
module Suvii
|
10
|
+
# Downloads and extracts an archive to a temp directory.
|
11
|
+
#
|
12
|
+
# @param url [String] URL of an archive to be fetched.
|
13
|
+
# @option (see Cache.fetch)
|
14
|
+
# @option (see Http.save)
|
15
|
+
# @option (see Extract#initialize)
|
16
|
+
# @return [String] path to a temp directory with the archive being extracted.
|
17
|
+
def self.fetch(url, options = {})
|
18
|
+
path_to_archive = Cache.fetch(url, options) do |path|
|
19
|
+
Http.save(url, path, options)
|
20
|
+
end
|
21
|
+
|
22
|
+
extractor = Extract.class_for(path_to_archive).new(path_to_archive, options)
|
23
|
+
extractor.extract_to(Dir.mktmpdir)
|
24
|
+
end
|
25
|
+
|
26
|
+
# @return [Logger] instance of Logger compatible class.
|
27
|
+
def self.logger
|
28
|
+
@logger ||= Logger.new(STDOUT).tap do |logger|
|
29
|
+
logger.level = Logger::INFO
|
30
|
+
logger.formatter = proc { |_, _, _, msg| msg }
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
# Overrides default logger instance.
|
35
|
+
#
|
36
|
+
# @param logger [Logger] instance of Logger compatible class.
|
37
|
+
def self.logger=(logger)
|
38
|
+
@logger = logger
|
39
|
+
end
|
40
|
+
end
|
data/lib/suvii/cache.rb
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
require "cgi"
|
2
|
+
require "tmpdir"
|
3
|
+
|
4
|
+
module Suvii
|
5
|
+
class Cache
|
6
|
+
# Maps an archive URL to a path to its cached file.
|
7
|
+
# @note This method doesn't write to a disk. It is a responsibility of a block implementation.
|
8
|
+
#
|
9
|
+
# @param url [String] URL of an archive to be processed.
|
10
|
+
# @option options [String] :cache_path (random temporary directory) where the downloaded archive
|
11
|
+
# should be stored. You can provide path to a persistent folder to prevent downloading same
|
12
|
+
# files again.
|
13
|
+
# @yield a block if the archive was not previously cached.
|
14
|
+
# @yieldparam path [String] full path to the archive to be stored.
|
15
|
+
# @return [String] full path to the stored archive.
|
16
|
+
def self.fetch(url, options = {})
|
17
|
+
cache_path = options[:cache_path] || Dir.mktmpdir
|
18
|
+
escaped_url = CGI.escape(url)
|
19
|
+
archive_path = File.join(cache_path, escaped_url)
|
20
|
+
yield archive_path unless File.exist?(archive_path)
|
21
|
+
archive_path
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
module Suvii
|
2
|
+
class Extract
|
3
|
+
UnknownFormatError = Class.new(StandardError)
|
4
|
+
|
5
|
+
TARGZ_RE = /\.t(ar\.)?gz\z/
|
6
|
+
ZIP_RE = /\.zip\z/
|
7
|
+
|
8
|
+
# Detects proper class for given archive path.
|
9
|
+
#
|
10
|
+
# @return [Targz, Zip]
|
11
|
+
# @param source [String] local path to an archive.
|
12
|
+
# @raise [UnknownFormatError] when archive format is not supported.
|
13
|
+
def self.class_for(source)
|
14
|
+
case source
|
15
|
+
when TARGZ_RE then Targz
|
16
|
+
when ZIP_RE then Zip
|
17
|
+
else raise UnknownFormatError, "unknown format for #{source}"
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
# @return [String]
|
22
|
+
attr_reader :source
|
23
|
+
|
24
|
+
# @return [Integer, nil]
|
25
|
+
attr_reader :strip_components
|
26
|
+
|
27
|
+
# @param source [String] local path to an archive.
|
28
|
+
# @option options [Integer] :strip_components (nil, i.e. no skipping) specifies number of top-level
|
29
|
+
# directories to be skipped during the archive extraction. Same as `strip-components` option
|
30
|
+
# for GNU tar.
|
31
|
+
def initialize(source, options = {})
|
32
|
+
@source = source
|
33
|
+
@strip_components = options[:strip_components]
|
34
|
+
end
|
35
|
+
|
36
|
+
# Performs archive extraction.
|
37
|
+
#
|
38
|
+
# @param destination [String] directory where the archive should be extracted.
|
39
|
+
# @return [String] destination.
|
40
|
+
def extract_to(destination)
|
41
|
+
raise NotImplementedError
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
def path_with_stripped_components(path)
|
47
|
+
if strip_components
|
48
|
+
segments = path.split(File::SEPARATOR)
|
49
|
+
segments[strip_components..-1].join(File::SEPARATOR)
|
50
|
+
else
|
51
|
+
path
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
require "suvii/extract/targz"
|
58
|
+
require "suvii/extract/zip"
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require "fileutils"
|
2
|
+
require "zlib"
|
3
|
+
require "rubygems/package"
|
4
|
+
|
5
|
+
module Suvii
|
6
|
+
class Extract
|
7
|
+
class Targz < Extract
|
8
|
+
CHUNK_SIZE = 65_536
|
9
|
+
DIR_UP = ".."
|
10
|
+
PAX_GLOBAL_HEADER = "pax_global_header"
|
11
|
+
|
12
|
+
# can't use IO.copy_stream because TarReader::Entry#read has different arity when IO#read
|
13
|
+
def self.copy_stream(tar_io, destination)
|
14
|
+
File.open(destination, "wb") do |destination_file|
|
15
|
+
until tar_io.eof?
|
16
|
+
destination_file.write(tar_io.read(CHUNK_SIZE))
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
# (see Extract#extract_to)
|
22
|
+
def extract_to(destination)
|
23
|
+
Zlib::GzipReader.open(source) do |gz|
|
24
|
+
Gem::Package::TarReader.new(gz) do |tar|
|
25
|
+
tar.each do |tarfile|
|
26
|
+
next if tarfile.full_name == PAX_GLOBAL_HEADER
|
27
|
+
|
28
|
+
if tarfile.full_name.include?(DIR_UP)
|
29
|
+
raise SecurityError, "can't write outside of destination directory, entry filename is #{tarfile.full_name.inspect}"
|
30
|
+
end
|
31
|
+
|
32
|
+
path = path_with_stripped_components(tarfile.full_name)
|
33
|
+
destination_file = File.join(destination, path)
|
34
|
+
|
35
|
+
if tarfile.directory?
|
36
|
+
FileUtils.mkdir_p(destination_file)
|
37
|
+
else
|
38
|
+
destination_directory = File.dirname(destination_file)
|
39
|
+
FileUtils.mkdir_p(destination_directory)
|
40
|
+
Targz.copy_stream(tarfile, destination_file)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
destination
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require "zip"
|
2
|
+
|
3
|
+
module Suvii
|
4
|
+
class Extract
|
5
|
+
class Zip < Extract
|
6
|
+
# (see Extract#extract_to)
|
7
|
+
def extract_to(destination)
|
8
|
+
::Zip::File.open(source) do |zip_file|
|
9
|
+
zip_file.each do |entry|
|
10
|
+
path = path_with_stripped_components(entry.name)
|
11
|
+
destination_file = File.join(destination, path)
|
12
|
+
entry.extract(destination_file)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
destination
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
data/lib/suvii/http.rb
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
require "open-uri"
|
2
|
+
|
3
|
+
module Suvii
|
4
|
+
# Thin wrapper around {https://github.com/ruby/ruby/blob/trunk/lib/open-uri.rb OpenURI},
|
5
|
+
# therefore proxy can be configured via standard env variables.
|
6
|
+
class Http
|
7
|
+
# Saves a content of the url to a file.
|
8
|
+
#
|
9
|
+
# @param url [String] URL to be fetched.
|
10
|
+
# @param destination [String] path to a file to be created with the URL content.
|
11
|
+
# @option options [Integer] :max_attempts_for_200_response (1, i.e. no retries) number of times
|
12
|
+
# to fetch a remote resource until get a 200 response.
|
13
|
+
# @raise [OpenURI::HTTPError]
|
14
|
+
# @return [true]
|
15
|
+
def self.save(url, destination, options = {})
|
16
|
+
attempts ||= 1
|
17
|
+
uri = URI.parse(url)
|
18
|
+
Suvii.logger.info("downloading '#{url}'")
|
19
|
+
IO.copy_stream(uri.open, destination)
|
20
|
+
true
|
21
|
+
rescue OpenURI::HTTPError => e
|
22
|
+
if attempts < (options[:max_attempts_for_200_response] || 1)
|
23
|
+
Suvii.logger.warn("failed to download '#{url}': #{e.message}")
|
24
|
+
attempts += 1
|
25
|
+
retry
|
26
|
+
else
|
27
|
+
raise
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
data/suvii.gemspec
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
Gem::Specification.new do |spec|
|
2
|
+
spec.name = "suvii"
|
3
|
+
spec.version = "0.1.0"
|
4
|
+
spec.authors = ["Andrii Malyshko"]
|
5
|
+
spec.email = ["mail@nashbridges.me"]
|
6
|
+
spec.description = "Read from a remote tar.gz/zip archive easily"
|
7
|
+
spec.summary = spec.description
|
8
|
+
spec.homepage = "https://github.com/torba-rb/suvii"
|
9
|
+
spec.license = "MIT"
|
10
|
+
|
11
|
+
spec.files = `git ls-files`.split($/)
|
12
|
+
spec.executables = spec.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
13
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
14
|
+
spec.require_paths = ["lib"]
|
15
|
+
|
16
|
+
spec.add_dependency "rubyzip", "~> 1.0"
|
17
|
+
|
18
|
+
spec.add_development_dependency "rake"
|
19
|
+
spec.add_development_dependency "bundler", "~> 1.6"
|
20
|
+
spec.add_development_dependency "minitest", "~> 5.4"
|
21
|
+
spec.add_development_dependency "webmock", "~> 1.24"
|
22
|
+
spec.add_development_dependency "assert_dirs_equal", "~> 0.2"
|
23
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require "test_helper"
|
2
|
+
|
3
|
+
module Suvii
|
4
|
+
class AcceptanceTest < Minitest::Test
|
5
|
+
attr_reader :destination
|
6
|
+
|
7
|
+
def teardown
|
8
|
+
FileUtils.rm_rf(destination)
|
9
|
+
end
|
10
|
+
|
11
|
+
def test_zip
|
12
|
+
@destination = Suvii.fetch(
|
13
|
+
"https://github.com/torba-rb/Trumbowyg/archive/1.1.7.zip",
|
14
|
+
strip_components: 1,
|
15
|
+
max_attempts_for_200_response: 2
|
16
|
+
)
|
17
|
+
assert_dirs_equal "test/fixtures/results/Trumbowyg-1.1.7", destination
|
18
|
+
end
|
19
|
+
|
20
|
+
def test_targz
|
21
|
+
@destination = Suvii.fetch(
|
22
|
+
"https://github.com/torba-rb/Trumbowyg/archive/1.1.7.tar.gz",
|
23
|
+
strip_components: 1,
|
24
|
+
max_attempts_for_200_response: 2
|
25
|
+
)
|
26
|
+
assert_dirs_equal "test/fixtures/results/Trumbowyg-1.1.7", destination
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|