simple-images-downloader 1.0.3 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5d6712c3ea229e3cb09526ef0f0144f79fa9dcf1d7af6f20b23a83502ea34be1
4
- data.tar.gz: b1147429efdf91682067decf309aec22050f2ed835b0b0261560f93fd6a63aa6
3
+ metadata.gz: 902c3b0e5fe1566296ac139123707162e33ded87accc9ff5705231fb3fa96f4f
4
+ data.tar.gz: c2812c8d700ca81d1f29b6a29d0063aef74b9dfd3162c652453733870b8073fe
5
5
  SHA512:
6
- metadata.gz: b3cf6db2633b3ebf0e094482079193fa2dbcb75e169c70ea3778e9c46b00abbdae4089378c36d8570630aa0698ca5343ee1a97e67f6e8e193c1d234c240bb35f
7
- data.tar.gz: cd9645e49d0a1658c74a6acde0a5608bd5a79fea67acda6235057c018066aaee7b5352ca150a5bf77d926189cd41dfb43950e62f0257edab98f31fc25f7a3f1b
6
+ metadata.gz: 5501d04aa338c0f992d391d94a5b29e8b63a410938c7a8f31941ed3178b2efa63a6ef84b9bb419f87278880197498367eeb235dea017509c57c708f758f7b4ab
7
+ data.tar.gz: 26ba7c6dbd4cfdf1e34750ce6b3f7e99650511ba56597a7c8ab1ee7950c210f7919f0a1104053fcc6195bec822b215678e9c8bc5015fee9d6fc8ae6eb27612e7
data/CHANGELOG.md CHANGED
@@ -1,6 +1,12 @@
1
+ ## 1.1.0 - 2023-09-30
2
+ - Add validation of downloaded content over path validation
3
+ - Add ability to extend mime types from configuration
4
+ - Removed zeitwerk
5
+ - Refactored main interface
6
+
1
7
  ## 1.0.3 - 2023-09-27
2
8
  - Corrected destination validation in SimpleImagesDownloader::Dispenser
3
- - Corrected source file validation in SimpleImagesDownloader::SourceFiles
9
+ - Corrected source file validation in SimpleImagesDownloader::SourceFile
4
10
  - Refactored
5
11
  - Fixed error message when destination is invalid
6
12
 
data/Gemfile.lock CHANGED
@@ -1,8 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- simple-images-downloader (1.0.3)
5
- zeitwerk (~> 2.4.0)
4
+ simple-images-downloader (1.1.0)
6
5
 
7
6
  GEM
8
7
  remote: https://rubygems.org/
@@ -76,7 +75,6 @@ GEM
76
75
  addressable (>= 2.3.6)
77
76
  crack (>= 0.3.2)
78
77
  hashdiff (>= 0.4.0, < 2.0.0)
79
- zeitwerk (2.4.2)
80
78
 
81
79
  PLATFORMS
82
80
  ruby
data/README.md CHANGED
@@ -33,7 +33,7 @@ Or install it yourself as:
33
33
 
34
34
  The gem is provided with executable.
35
35
 
36
- You can either run plain commmand with file containing list of url:
36
+ You can either run plain command with file containing list of url:
37
37
 
38
38
  ```bash
39
39
  simple-images-downloader <path_to_file>
@@ -67,6 +67,7 @@ Downloading https://test-for-simple-images-downloader.s3.eu-central-1.amazonaws.
67
67
  Downloading is finished
68
68
  ```
69
69
 
70
+ ## Configuration
70
71
  **By default the images stored in a directory the script is run.**
71
72
  You can also configure the place where the images would be stored to:
72
73
 
@@ -78,6 +79,18 @@ You can also configure the place where the images would be stored to:
78
79
  [2] pry(main)* end
79
80
  ```
80
81
 
82
+ **By default the gem downloads only images with mime types: image/avif, image/gif, image/apng, image/jpg, image/jpeg, image/png, image/svg+xml, image/webp.**
83
+ You can also configure the mime types to download images with:
84
+
85
+
86
+ ```ruby
87
+ [1] pry(main)> require 'simple_images_downloader'
88
+
89
+ [2] pry(main)> SimpleImagesDownloader::Configuration.configure do |config|
90
+ [2] pry(main)* # You must use RFC 1341 - MIME (Multipurpose Internet Mail Extensions) format
91
+ [2] pry(main)* config.valid_mime_types = %w(image/png image/jpg image/jpeg)
92
+ [2] pry(main)* end
93
+ ```
81
94
  ## Development
82
95
 
83
96
  After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
@@ -6,6 +6,7 @@ module SimpleImagesDownloader
6
6
 
7
7
  ACCESSORS = %i[
8
8
  destination
9
+ valid_mime_types
9
10
  ].freeze
10
11
 
11
12
  REQUEST_OPTIONS = {
@@ -15,10 +16,31 @@ module SimpleImagesDownloader
15
16
  read_timeout: 30
16
17
  }.freeze
17
18
 
19
+ # https://developer.mozilla.org/en-US/docs/Web/Media/Formats/Image_types#common_image_file_types
20
+ DEFAULT_VALID_MIME_TYPES_MAP = {
21
+ 'image/avif' => true,
22
+ 'image/gif' => true,
23
+ 'image/apng' => true,
24
+ 'image/jpg' => true,
25
+ 'image/jpeg' => true,
26
+ 'image/png' => true,
27
+ 'image/svg+xml' => true,
28
+ 'image/webp' => true
29
+ }.freeze
30
+
31
+ DEFAULT_DESTINATION = './'
32
+
18
33
  attr_accessor(*ACCESSORS)
19
34
 
20
35
  def initialize
21
- @destination = './'
36
+ @destination = DEFAULT_DESTINATION
37
+ @valid_mime_types = DEFAULT_VALID_MIME_TYPES_MAP.keys
38
+ end
39
+
40
+ def valid_mime_types=(value)
41
+ raise BaseError, 'valid_mime_types must be an array' unless value.is_a?(Array)
42
+
43
+ @valid_mime_types = value
22
44
  end
23
45
 
24
46
  def self.configure
@@ -14,7 +14,7 @@ module SimpleImagesDownloader
14
14
  end
15
15
 
16
16
  def place
17
- validate!(destination_dir)
17
+ validate!({ path: destination_dir })
18
18
 
19
19
  FileUtils.mv @source, target
20
20
  end
@@ -2,22 +2,31 @@
2
2
 
3
3
  module SimpleImagesDownloader
4
4
  class Downloader
5
- def initialize(uri)
6
- @uri = uri
5
+ include Validatable
6
+
7
+ def initialize(uri, client = Client.new, validators = [MimeTypeValidator.new])
8
+ @uri = uri
9
+ @client = client
10
+ @validators = validators
7
11
  end
8
12
 
9
13
  def download
10
14
  puts "Downloading #{@uri}"
11
15
 
12
- io = Client.new.open(@uri)
16
+ io = @client.open(@uri)
17
+
18
+ raise Errors::EmptyResponse, @uri if io.nil?
19
+
20
+ validate!({ path: @uri.to_s, io: io })
13
21
 
14
- downloaded_file = StringioToTempfile.convert(io) unless io.nil?
22
+ tempfile = StringioToTempfile.convert(io)
15
23
 
16
- Dispenser.new(downloaded_file, @uri.path).place
24
+ Dispenser.new(tempfile, @uri.path).place
17
25
 
18
26
  puts 'Downloading is finished'
19
27
  ensure
20
- downloaded_file&.close
28
+ io&.close
29
+ tempfile&.close
21
30
  end
22
31
  end
23
32
  end
@@ -25,9 +25,9 @@ module SimpleImagesDownloader
25
25
  end
26
26
  end
27
27
 
28
- class MissingImageInPath < BaseError
29
- def initialize(path)
30
- message = "The path doesn't contain image #{path}"
28
+ class BadMimeType < BaseError
29
+ def initialize(path, mime_type)
30
+ message = "The image with path: #{path} has wrong mime type #{mime_type}"
31
31
  super(message)
32
32
  end
33
33
  end
@@ -66,5 +66,12 @@ module SimpleImagesDownloader
66
66
  super(message)
67
67
  end
68
68
  end
69
+
70
+ class EmptyResponse < BaseError
71
+ def initialize(uri)
72
+ message = "Nothing returned from request #{uri}"
73
+ super(message)
74
+ end
75
+ end
69
76
  end
70
77
  end
@@ -2,19 +2,12 @@
2
2
 
3
3
  module SimpleImagesDownloader
4
4
  class Line
5
- include Validatable
6
-
7
- def initialize(string, validators = [ImagePathValidator.new])
8
- @string = string
9
- @validators = validators
5
+ def initialize(string)
6
+ @string = string
10
7
  end
11
8
 
12
9
  def uri
13
- parsed_uri = URI.parse(@string)
14
-
15
- validate!(@string)
16
-
17
- parsed_uri
10
+ URI.parse(@string)
18
11
  rescue URI::Error
19
12
  raise Errors::BadUrl, @string
20
13
  end
@@ -10,7 +10,7 @@ module SimpleImagesDownloader
10
10
  end
11
11
 
12
12
  def each_line(&block)
13
- validate!(@path)
13
+ validate!({ path: @path })
14
14
 
15
15
  begin
16
16
  file.each(chomp: true, &block)
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SimpleImagesDownloader
4
+ module Strategies
5
+ class FromFileStrategy < Strategy
6
+ def initialize(path)
7
+ super
8
+ @path = path
9
+ end
10
+
11
+ def process
12
+ source_file = SourceFile.new(@path)
13
+
14
+ source_file.each_line do |line|
15
+ uri = Line.new(line).uri
16
+ Downloader.new(uri).download
17
+ rescue Errors::BaseError => e
18
+ puts e.message
19
+ next
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SimpleImagesDownloader
4
+ module Strategies
5
+ class FromUrlStrategy < Strategy
6
+ def initialize(url)
7
+ super
8
+ @url = url
9
+ end
10
+
11
+ def process
12
+ uri = Line.new(@url).uri
13
+
14
+ Downloader.new(uri).download
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SimpleImagesDownloader
4
+ module Strategies
5
+ class Strategy
6
+ def initialize(_)
7
+ end
8
+
9
+ def process
10
+ raise NotImplementedError, 'must be implemented in subclass'
11
+ end
12
+ end
13
+ end
14
+ end
@@ -9,8 +9,6 @@ module SimpleImagesDownloader
9
9
 
10
10
  IO.copy_stream(stringio, tempfile)
11
11
 
12
- stringio.close
13
-
14
12
  OpenURI::Meta.init tempfile, stringio
15
13
 
16
14
  tempfile
@@ -3,9 +3,11 @@
3
3
  module SimpleImagesDownloader
4
4
  module Validatable
5
5
  class DestinationValidator < Validator
6
- def validate(destination_dir)
7
- raise Errors::DestinationIsNotDirectory, destination_dir unless File.directory?(destination_dir)
8
- raise Errors::DestinationIsNotWritable, destination_dir unless File.writable?(destination_dir)
6
+ def validate(options)
7
+ path = options[:path]
8
+
9
+ raise Errors::DestinationIsNotDirectory, path unless File.directory?(path)
10
+ raise Errors::DestinationIsNotWritable, path unless File.writable?(path)
9
11
  end
10
12
  end
11
13
  end
@@ -3,10 +3,10 @@
3
3
  module SimpleImagesDownloader
4
4
  module Validatable
5
5
  class FileAccessibilityValidator < Validator
6
- def validate(path)
7
- return if File.readable?(path)
6
+ def validate(options)
7
+ return if File.readable?(options[:path])
8
8
 
9
- raise Errors::PermissionsError, path
9
+ raise Errors::PermissionsError, options[:path]
10
10
  end
11
11
  end
12
12
  end
@@ -3,10 +3,10 @@
3
3
  module SimpleImagesDownloader
4
4
  module Validatable
5
5
  class FilePersistanceValidator < Validator
6
- def validate(path)
7
- return if File.exist?(path)
6
+ def validate(options)
7
+ return if File.exist?(options[:path])
8
8
 
9
- raise Errors::MissingFileError, path
9
+ raise Errors::MissingFileError, options[:path]
10
10
  end
11
11
  end
12
12
  end
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SimpleImagesDownloader
4
+ module Validatable
5
+ class MimeTypeValidator < Validator
6
+ extend Forwardable
7
+
8
+ def_delegator 'SimpleImagesDownloader::Configuration', :valid_mime_types, :valid_mime_types
9
+
10
+ def validate(options)
11
+ path = options[:path]
12
+ io = options[:io]
13
+
14
+ mime_type = mime_type_of(io)
15
+
16
+ return if Configuration::DEFAULT_VALID_MIME_TYPES_MAP[mime_type]
17
+
18
+ raise Errors::BadMimeType.new(path, mime_type)
19
+ end
20
+
21
+ private
22
+
23
+ # Taken from Shrine https://github.com/shrinerb/shrine/blob/master/lib/shrine/plugins/determine_mime_type.rb#L94
24
+ def mime_type_of(io)
25
+ Open3.popen3('file --mime-type --brief -') do |stdin, stdout, stderr, thread|
26
+ copy_stream(from: io, to: stdin.binmode)
27
+
28
+ io.rewind
29
+ stdin.close
30
+
31
+ status = thread.value
32
+
33
+ validate_thread_status(status)
34
+ $stderr.print(stderr.read)
35
+
36
+ output = stdout.read.strip
37
+
38
+ validate_command_output(output)
39
+
40
+ output
41
+ end
42
+ end
43
+
44
+ def copy_stream(from:, to:)
45
+ IO.copy_stream(from, to)
46
+ rescue Errno::EPIPE # rubocop:disable Lint/SuppressedException
47
+ end
48
+
49
+ def validate_thread_status(status)
50
+ raise Errors::BaseError, "file command failed to spawn: #{stderr.read}" if status.nil?
51
+ raise Errors::BaseError, "file command failed: #{stderr.read}" unless status.success?
52
+ end
53
+
54
+ def validate_command_output(output)
55
+ raise Errors::BaseError, "file command failed: #{output}" if output.include?('cannot open')
56
+ end
57
+ end
58
+ end
59
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module SimpleImagesDownloader
4
- VERSION = '1.0.3'
4
+ VERSION = '1.1.0'
5
5
  end
@@ -4,27 +4,34 @@ require 'open-uri'
4
4
  require 'tempfile'
5
5
  require 'forwardable'
6
6
  require 'singleton'
7
- require 'zeitwerk'
8
-
9
- loader = Zeitwerk::Loader.for_gem
10
- loader.setup
7
+ require 'open3'
8
+ require_relative 'simple_images_downloader/version'
9
+ require_relative 'simple_images_downloader/errors'
10
+ require_relative 'simple_images_downloader/configuration'
11
+ require_relative 'simple_images_downloader/validatable'
12
+ require_relative 'simple_images_downloader/validatable/validator'
13
+ require_relative 'simple_images_downloader/validatable/destination_validator'
14
+ require_relative 'simple_images_downloader/validatable/file_accessibility_validator'
15
+ require_relative 'simple_images_downloader/validatable/file_persistance_validator'
16
+ require_relative 'simple_images_downloader/validatable/mime_type_validator'
17
+ require_relative 'simple_images_downloader/stringio_to_tempfile'
18
+ require_relative 'simple_images_downloader/client'
19
+ require_relative 'simple_images_downloader/source_file'
20
+ require_relative 'simple_images_downloader/runner'
21
+ require_relative 'simple_images_downloader/line'
22
+ require_relative 'simple_images_downloader/dispenser'
23
+ require_relative 'simple_images_downloader/strategies/strategy'
24
+ require_relative 'simple_images_downloader/downloader'
25
+ require_relative 'simple_images_downloader/strategies/from_file_strategy'
26
+ require_relative 'simple_images_downloader/strategies/from_url_strategy'
11
27
 
12
28
  module SimpleImagesDownloader
13
29
  def self.from_file(path)
14
- source_file = SourceFile.new(path)
15
-
16
- source_file.each_line do |line|
17
- uri = Line.new(line).uri
18
- Downloader.new(uri).download
19
- rescue Errors::BaseError => e
20
- puts e.message
21
- next
22
- end
30
+ SimpleImagesDownloader::Strategies::FromFileStrategy.new(path).process
23
31
  end
24
32
 
25
33
  def self.from_url(url)
26
- uri = Line.new(url).uri
27
- Downloader.new(uri).download
34
+ SimpleImagesDownloader::Strategies::FromUrlStrategy.new(url).process
28
35
  end
29
36
 
30
37
  def self.root
data/rubocop.yml CHANGED
@@ -69,6 +69,9 @@ Layout/LineLength:
69
69
  Metrics/BlockLength:
70
70
  Enabled: false
71
71
 
72
+ Metrics/MethodLength:
73
+ Max: 11
74
+
72
75
  Layout/EndAlignment:
73
76
  EnforcedStyleAlignWith: variable
74
77
  SupportedStylesAlignWith:
@@ -30,8 +30,6 @@ Gem::Specification.new do |spec|
30
30
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
31
31
  spec.require_paths = ['lib']
32
32
 
33
- spec.add_runtime_dependency 'zeitwerk', '~> 2.4.0'
34
-
35
33
  spec.add_development_dependency 'faker', '~> 2.14'
36
34
  spec.add_development_dependency 'pry', '~> 0.13.1'
37
35
  spec.add_development_dependency 'rake', '~> 12.0'
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: simple-images-downloader
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.3
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - IlkhamGaysin
@@ -10,20 +10,6 @@ bindir: exe
10
10
  cert_chain: []
11
11
  date: 2023-09-30 00:00:00.000000000 Z
12
12
  dependencies:
13
- - !ruby/object:Gem::Dependency
14
- name: zeitwerk
15
- requirement: !ruby/object:Gem::Requirement
16
- requirements:
17
- - - "~>"
18
- - !ruby/object:Gem::Version
19
- version: 2.4.0
20
- type: :runtime
21
- prerelease: false
22
- version_requirements: !ruby/object:Gem::Requirement
23
- requirements:
24
- - - "~>"
25
- - !ruby/object:Gem::Version
26
- version: 2.4.0
27
13
  - !ruby/object:Gem::Dependency
28
14
  name: faker
29
15
  requirement: !ruby/object:Gem::Requirement
@@ -214,12 +200,15 @@ files:
214
200
  - lib/simple_images_downloader/line.rb
215
201
  - lib/simple_images_downloader/runner.rb
216
202
  - lib/simple_images_downloader/source_file.rb
203
+ - lib/simple_images_downloader/strategies/from_file_strategy.rb
204
+ - lib/simple_images_downloader/strategies/from_url_strategy.rb
205
+ - lib/simple_images_downloader/strategies/strategy.rb
217
206
  - lib/simple_images_downloader/stringio_to_tempfile.rb
218
207
  - lib/simple_images_downloader/validatable.rb
219
208
  - lib/simple_images_downloader/validatable/destination_validator.rb
220
209
  - lib/simple_images_downloader/validatable/file_accessibility_validator.rb
221
210
  - lib/simple_images_downloader/validatable/file_persistance_validator.rb
222
- - lib/simple_images_downloader/validatable/image_path_validator.rb
211
+ - lib/simple_images_downloader/validatable/mime_type_validator.rb
223
212
  - lib/simple_images_downloader/validatable/validator.rb
224
213
  - lib/simple_images_downloader/version.rb
225
214
  - rubocop.yml
@@ -1,17 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module SimpleImagesDownloader
4
- module Validatable
5
- class ImagePathValidator < Validator
6
- VALID_EXTENSIONS = %w[.png .jpg .gif .jpeg].freeze
7
-
8
- def validate(path)
9
- extension = File.extname(path)
10
-
11
- return if VALID_EXTENSIONS.include?(extension)
12
-
13
- raise Errors::MissingImageInPath, path
14
- end
15
- end
16
- end
17
- end