suvii 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (150) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +14 -0
  3. data/.travis.yml +10 -0
  4. data/.yardopts +1 -0
  5. data/CHANGELOG.md +1 -0
  6. data/CONTRIBUTING.md +47 -0
  7. data/Gemfile +6 -0
  8. data/LICENSE.txt +22 -0
  9. data/README.md +51 -0
  10. data/Rakefile +7 -0
  11. data/lib/suvii.rb +40 -0
  12. data/lib/suvii/cache.rb +24 -0
  13. data/lib/suvii/extract.rb +58 -0
  14. data/lib/suvii/extract/targz.rb +49 -0
  15. data/lib/suvii/extract/zip.rb +19 -0
  16. data/lib/suvii/http.rb +31 -0
  17. data/suvii.gemspec +23 -0
  18. data/test/acceptance_test.rb +29 -0
  19. data/test/cache_test.rb +42 -0
  20. data/test/extract/targz_test.rb +28 -0
  21. data/test/extract/zip_test.rb +28 -0
  22. data/test/extract_test.rb +26 -0
  23. data/test/fixtures/results/Trumbowyg-1.1.7/.gitattributes +6 -0
  24. data/test/fixtures/results/Trumbowyg-1.1.7/.jshintrc +15 -0
  25. data/test/fixtures/results/Trumbowyg-1.1.7/Gulpfile.js +167 -0
  26. data/test/fixtures/results/Trumbowyg-1.1.7/LICENSE +21 -0
  27. data/test/fixtures/results/Trumbowyg-1.1.7/README.md +29 -0
  28. data/test/fixtures/results/Trumbowyg-1.1.7/bower.json +36 -0
  29. data/test/fixtures/results/Trumbowyg-1.1.7/dist/langs/ca.min.js +11 -0
  30. data/test/fixtures/results/Trumbowyg-1.1.7/dist/langs/da.min.js +9 -0
  31. data/test/fixtures/results/Trumbowyg-1.1.7/dist/langs/de.min.js +9 -0
  32. data/test/fixtures/results/Trumbowyg-1.1.7/dist/langs/es.min.js +9 -0
  33. data/test/fixtures/results/Trumbowyg-1.1.7/dist/langs/es_ar.min.js +9 -0
  34. data/test/fixtures/results/Trumbowyg-1.1.7/dist/langs/fa.min.js +10 -0
  35. data/test/fixtures/results/Trumbowyg-1.1.7/dist/langs/fi.min.js +9 -0
  36. data/test/fixtures/results/Trumbowyg-1.1.7/dist/langs/fr.min.js +10 -0
  37. data/test/fixtures/results/Trumbowyg-1.1.7/dist/langs/he.min.js +9 -0
  38. data/test/fixtures/results/Trumbowyg-1.1.7/dist/langs/id.min.js +11 -0
  39. data/test/fixtures/results/Trumbowyg-1.1.7/dist/langs/it.min.js +8 -0
  40. data/test/fixtures/results/Trumbowyg-1.1.7/dist/langs/ko.min.js +10 -0
  41. data/test/fixtures/results/Trumbowyg-1.1.7/dist/langs/pl.min.js +9 -0
  42. data/test/fixtures/results/Trumbowyg-1.1.7/dist/langs/pt.min.js +11 -0
  43. data/test/fixtures/results/Trumbowyg-1.1.7/dist/langs/ro.min.js +13 -0
  44. data/test/fixtures/results/Trumbowyg-1.1.7/dist/langs/ru.min.js +8 -0
  45. data/test/fixtures/results/Trumbowyg-1.1.7/dist/langs/tr.min.js +10 -0
  46. data/test/fixtures/results/Trumbowyg-1.1.7/dist/langs/zh_cn.min.js +10 -0
  47. data/test/fixtures/results/Trumbowyg-1.1.7/dist/plugins/base64/trumbowyg.base64.js +74 -0
  48. data/test/fixtures/results/Trumbowyg-1.1.7/dist/plugins/base64/trumbowyg.base64.min.js +1 -0
  49. data/test/fixtures/results/Trumbowyg-1.1.7/dist/plugins/colors/trumbowyg.colors.js +69 -0
  50. data/test/fixtures/results/Trumbowyg-1.1.7/dist/plugins/colors/trumbowyg.colors.min.js +1 -0
  51. data/test/fixtures/results/Trumbowyg-1.1.7/dist/plugins/colors/ui/images/icons-2x.png +0 -0
  52. data/test/fixtures/results/Trumbowyg-1.1.7/dist/plugins/colors/ui/images/icons.png +0 -0
  53. data/test/fixtures/results/Trumbowyg-1.1.7/dist/plugins/colors/ui/trumbowyg.colors.css +55 -0
  54. data/test/fixtures/results/Trumbowyg-1.1.7/dist/plugins/colors/ui/trumbowyg.colors.min.css +2 -0
  55. data/test/fixtures/results/Trumbowyg-1.1.7/dist/plugins/upload/trumbowyg.upload.js +140 -0
  56. data/test/fixtures/results/Trumbowyg-1.1.7/dist/plugins/upload/trumbowyg.upload.min.js +1 -0
  57. data/test/fixtures/results/Trumbowyg-1.1.7/dist/trumbowyg.js +1124 -0
  58. data/test/fixtures/results/Trumbowyg-1.1.7/dist/trumbowyg.min.js +2 -0
  59. data/test/fixtures/results/Trumbowyg-1.1.7/dist/ui/images/icons-2x.png +0 -0
  60. data/test/fixtures/results/Trumbowyg-1.1.7/dist/ui/images/icons.png +0 -0
  61. data/test/fixtures/results/Trumbowyg-1.1.7/dist/ui/trumbowyg.css +471 -0
  62. data/test/fixtures/results/Trumbowyg-1.1.7/dist/ui/trumbowyg.min.css +2 -0
  63. data/test/fixtures/results/Trumbowyg-1.1.7/examples/css/main.css +14 -0
  64. data/test/fixtures/results/Trumbowyg-1.1.7/examples/index.html +41 -0
  65. data/test/fixtures/results/Trumbowyg-1.1.7/examples/plugins/colors.html +49 -0
  66. data/test/fixtures/results/Trumbowyg-1.1.7/index.html +252 -0
  67. data/test/fixtures/results/Trumbowyg-1.1.7/package.json +48 -0
  68. data/test/fixtures/results/Trumbowyg-1.1.7/plugins/base64/trumbowyg.base64.js +74 -0
  69. data/test/fixtures/results/Trumbowyg-1.1.7/plugins/colors/Gulpfile.js +101 -0
  70. data/test/fixtures/results/Trumbowyg-1.1.7/plugins/colors/trumbowyg.colors.js +69 -0
  71. data/test/fixtures/results/Trumbowyg-1.1.7/plugins/colors/ui/images/icons-2x/backcolor.png +0 -0
  72. data/test/fixtures/results/Trumbowyg-1.1.7/plugins/colors/ui/images/icons-2x/forecolor.png +0 -0
  73. data/test/fixtures/results/Trumbowyg-1.1.7/plugins/colors/ui/images/icons/backcolor.png +0 -0
  74. data/test/fixtures/results/Trumbowyg-1.1.7/plugins/colors/ui/images/icons/forecolor.png +0 -0
  75. data/test/fixtures/results/Trumbowyg-1.1.7/plugins/colors/ui/sass/trumbowyg.colors.scss +65 -0
  76. data/test/fixtures/results/Trumbowyg-1.1.7/plugins/upload/trumbowyg.upload.js +140 -0
  77. data/test/fixtures/results/Trumbowyg-1.1.7/plugins/upload/trumbowyg.upload.php +49 -0
  78. data/test/fixtures/results/Trumbowyg-1.1.7/src/langs/ca.js +58 -0
  79. data/test/fixtures/results/Trumbowyg-1.1.7/src/langs/da.js +56 -0
  80. data/test/fixtures/results/Trumbowyg-1.1.7/src/langs/de.js +56 -0
  81. data/test/fixtures/results/Trumbowyg-1.1.7/src/langs/en.js +14 -0
  82. data/test/fixtures/results/Trumbowyg-1.1.7/src/langs/es.js +56 -0
  83. data/test/fixtures/results/Trumbowyg-1.1.7/src/langs/es_ar.js +56 -0
  84. data/test/fixtures/results/Trumbowyg-1.1.7/src/langs/fa.js +57 -0
  85. data/test/fixtures/results/Trumbowyg-1.1.7/src/langs/fi.js +56 -0
  86. data/test/fixtures/results/Trumbowyg-1.1.7/src/langs/fr.js +57 -0
  87. data/test/fixtures/results/Trumbowyg-1.1.7/src/langs/he.js +58 -0
  88. data/test/fixtures/results/Trumbowyg-1.1.7/src/langs/id.js +58 -0
  89. data/test/fixtures/results/Trumbowyg-1.1.7/src/langs/it.js +55 -0
  90. data/test/fixtures/results/Trumbowyg-1.1.7/src/langs/ko.js +57 -0
  91. data/test/fixtures/results/Trumbowyg-1.1.7/src/langs/pl.js +56 -0
  92. data/test/fixtures/results/Trumbowyg-1.1.7/src/langs/pt.js +58 -0
  93. data/test/fixtures/results/Trumbowyg-1.1.7/src/langs/ro.js +60 -0
  94. data/test/fixtures/results/Trumbowyg-1.1.7/src/langs/ru.js +55 -0
  95. data/test/fixtures/results/Trumbowyg-1.1.7/src/langs/tr.js +57 -0
  96. data/test/fixtures/results/Trumbowyg-1.1.7/src/langs/zh_cn.js +57 -0
  97. data/test/fixtures/results/Trumbowyg-1.1.7/src/trumbowyg.js +1113 -0
  98. data/test/fixtures/results/Trumbowyg-1.1.7/src/ui/images/icons-2x/attachement.png +0 -0
  99. data/test/fixtures/results/Trumbowyg-1.1.7/src/ui/images/icons-2x/barre.png +0 -0
  100. data/test/fixtures/results/Trumbowyg-1.1.7/src/ui/images/icons-2x/bold.png +0 -0
  101. data/test/fixtures/results/Trumbowyg-1.1.7/src/ui/images/icons-2x/center-align.png +0 -0
  102. data/test/fixtures/results/Trumbowyg-1.1.7/src/ui/images/icons-2x/close.png +0 -0
  103. data/test/fixtures/results/Trumbowyg-1.1.7/src/ui/images/icons-2x/formatting.png +0 -0
  104. data/test/fixtures/results/Trumbowyg-1.1.7/src/ui/images/icons-2x/fullscreen-exit.png +0 -0
  105. data/test/fixtures/results/Trumbowyg-1.1.7/src/ui/images/icons-2x/fullscreen.png +0 -0
  106. data/test/fixtures/results/Trumbowyg-1.1.7/src/ui/images/icons-2x/gras.png +0 -0
  107. data/test/fixtures/results/Trumbowyg-1.1.7/src/ui/images/icons-2x/horizontal-rule.png +0 -0
  108. data/test/fixtures/results/Trumbowyg-1.1.7/src/ui/images/icons-2x/image.png +0 -0
  109. data/test/fixtures/results/Trumbowyg-1.1.7/src/ui/images/icons-2x/italic.png +0 -0
  110. data/test/fixtures/results/Trumbowyg-1.1.7/src/ui/images/icons-2x/justify-align.png +0 -0
  111. data/test/fixtures/results/Trumbowyg-1.1.7/src/ui/images/icons-2x/left-align.png +0 -0
  112. data/test/fixtures/results/Trumbowyg-1.1.7/src/ui/images/icons-2x/link.png +0 -0
  113. data/test/fixtures/results/Trumbowyg-1.1.7/src/ui/images/icons-2x/ordered-list.png +0 -0
  114. data/test/fixtures/results/Trumbowyg-1.1.7/src/ui/images/icons-2x/right-align.png +0 -0
  115. data/test/fixtures/results/Trumbowyg-1.1.7/src/ui/images/icons-2x/souligne.png +0 -0
  116. data/test/fixtures/results/Trumbowyg-1.1.7/src/ui/images/icons-2x/strikethrough.png +0 -0
  117. data/test/fixtures/results/Trumbowyg-1.1.7/src/ui/images/icons-2x/underline.png +0 -0
  118. data/test/fixtures/results/Trumbowyg-1.1.7/src/ui/images/icons-2x/unordered-list.png +0 -0
  119. data/test/fixtures/results/Trumbowyg-1.1.7/src/ui/images/icons-2x/video.png +0 -0
  120. data/test/fixtures/results/Trumbowyg-1.1.7/src/ui/images/icons-2x/view-html.png +0 -0
  121. data/test/fixtures/results/Trumbowyg-1.1.7/src/ui/images/icons/attachement.png +0 -0
  122. data/test/fixtures/results/Trumbowyg-1.1.7/src/ui/images/icons/barre.png +0 -0
  123. data/test/fixtures/results/Trumbowyg-1.1.7/src/ui/images/icons/bold.png +0 -0
  124. data/test/fixtures/results/Trumbowyg-1.1.7/src/ui/images/icons/center-align.png +0 -0
  125. data/test/fixtures/results/Trumbowyg-1.1.7/src/ui/images/icons/close.png +0 -0
  126. data/test/fixtures/results/Trumbowyg-1.1.7/src/ui/images/icons/formatting.png +0 -0
  127. data/test/fixtures/results/Trumbowyg-1.1.7/src/ui/images/icons/fullscreen-exit.png +0 -0
  128. data/test/fixtures/results/Trumbowyg-1.1.7/src/ui/images/icons/fullscreen.png +0 -0
  129. data/test/fixtures/results/Trumbowyg-1.1.7/src/ui/images/icons/gras.png +0 -0
  130. data/test/fixtures/results/Trumbowyg-1.1.7/src/ui/images/icons/horizontal-rule.png +0 -0
  131. data/test/fixtures/results/Trumbowyg-1.1.7/src/ui/images/icons/image.png +0 -0
  132. data/test/fixtures/results/Trumbowyg-1.1.7/src/ui/images/icons/italic.png +0 -0
  133. data/test/fixtures/results/Trumbowyg-1.1.7/src/ui/images/icons/justify-align.png +0 -0
  134. data/test/fixtures/results/Trumbowyg-1.1.7/src/ui/images/icons/left-align.png +0 -0
  135. data/test/fixtures/results/Trumbowyg-1.1.7/src/ui/images/icons/link.png +0 -0
  136. data/test/fixtures/results/Trumbowyg-1.1.7/src/ui/images/icons/ordered-list.png +0 -0
  137. data/test/fixtures/results/Trumbowyg-1.1.7/src/ui/images/icons/right-align.png +0 -0
  138. data/test/fixtures/results/Trumbowyg-1.1.7/src/ui/images/icons/souligne.png +0 -0
  139. data/test/fixtures/results/Trumbowyg-1.1.7/src/ui/images/icons/strikethrough.png +0 -0
  140. data/test/fixtures/results/Trumbowyg-1.1.7/src/ui/images/icons/underline.png +0 -0
  141. data/test/fixtures/results/Trumbowyg-1.1.7/src/ui/images/icons/unordered-list.png +0 -0
  142. data/test/fixtures/results/Trumbowyg-1.1.7/src/ui/images/icons/video.png +0 -0
  143. data/test/fixtures/results/Trumbowyg-1.1.7/src/ui/images/icons/view-html.png +0 -0
  144. data/test/fixtures/results/Trumbowyg-1.1.7/src/ui/sass/mixins/_sprite-pos.scss +7 -0
  145. data/test/fixtures/results/Trumbowyg-1.1.7/src/ui/sass/trumbowyg.scss +564 -0
  146. data/test/fixtures/trumbowyg.tar.gz +0 -0
  147. data/test/fixtures/trumbowyg.zip +0 -0
  148. data/test/http_test.rb +61 -0
  149. data/test/test_helper.rb +24 -0
  150. 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
@@ -0,0 +1,14 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ *.bundle
11
+ *.so
12
+ *.o
13
+ *.a
14
+ mkmf.log
data/.travis.yml ADDED
@@ -0,0 +1,10 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.3
4
+ - 2.0.0
5
+ - 2.1
6
+ - 2.2
7
+ - 2.3.0
8
+ - jruby-19mode
9
+ - rbx-2
10
+ bundler_args: --without doc debug
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
@@ -0,0 +1,6 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
4
+
5
+ gem "pry", :group => :debug
6
+ gem "yard", "~> 0.8", :group => :doc
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
@@ -0,0 +1,7 @@
1
+ require "bundler/gem_tasks"
2
+
3
+ task :default => :test
4
+ task :test do
5
+ $LOAD_PATH.unshift("test")
6
+ Dir.glob("./test/**/*_test.rb").each { |file| require file}
7
+ end
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
@@ -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