simple-images-downloader 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/gem_tasks'
4
+ require 'rspec/core/rake_task'
5
+
6
+ RSpec::Core::RakeTask.new(:spec)
7
+
8
+ task default: :spec
@@ -0,0 +1,118 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ #
5
+ # This file was generated by Bundler.
6
+ #
7
+ # The application 'bundle' is installed as part of a gem, and
8
+ # this file is here to facilitate running it.
9
+ #
10
+
11
+ require 'rubygems'
12
+
13
+ m = Module.new do
14
+ module_function
15
+
16
+ def invoked_as_script?
17
+ File.expand_path($PROGRAM_NAME) == File.expand_path(__FILE__)
18
+ end
19
+
20
+ def env_var_version
21
+ ENV['BUNDLER_VERSION']
22
+ end
23
+
24
+ def cli_arg_version
25
+ return unless invoked_as_script? # don't want to hijack other binstubs
26
+ return unless 'update'.start_with?(ARGV.first || ' ') # must be running `bundle update`
27
+
28
+ bundler_version = nil
29
+ update_index = nil
30
+ ARGV.each_with_index do |a, i|
31
+ bundler_version = a if update_index && update_index.succ == i && a =~ Gem::Version::ANCHORED_VERSION_PATTERN
32
+ next unless a =~ /\A--bundler(?:[= ](#{Gem::Version::VERSION_PATTERN}))?\z/
33
+
34
+ bundler_version = Regexp.last_match(1)
35
+ update_index = i
36
+ end
37
+ bundler_version
38
+ end
39
+
40
+ def gemfile
41
+ gemfile = ENV['BUNDLE_GEMFILE']
42
+ return gemfile if gemfile && !gemfile.empty?
43
+
44
+ File.expand_path('../Gemfile', __dir__)
45
+ end
46
+
47
+ def lockfile
48
+ lockfile =
49
+ case File.basename(gemfile)
50
+ when 'gems.rb' then gemfile.sub(/\.rb$/, gemfile)
51
+ else "#{gemfile}.lock"
52
+ end
53
+ File.expand_path(lockfile)
54
+ end
55
+
56
+ def lockfile_version
57
+ return unless File.file?(lockfile)
58
+
59
+ lockfile_contents = File.read(lockfile)
60
+ return unless lockfile_contents =~ /\n\nBUNDLED WITH\n\s{2,}(#{Gem::Version::VERSION_PATTERN})\n/
61
+
62
+ Regexp.last_match(1)
63
+ end
64
+
65
+ def bundler_version
66
+ @bundler_version ||=
67
+ env_var_version || cli_arg_version ||
68
+ lockfile_version
69
+ end
70
+
71
+ def bundler_requirement
72
+ return "#{Gem::Requirement.default}.a" unless bundler_version
73
+
74
+ bundler_gem_version = Gem::Version.new(bundler_version)
75
+
76
+ requirement = bundler_gem_version.approximate_recommendation
77
+
78
+ return requirement unless Gem::Version.new(Gem::VERSION) < Gem::Version.new('2.7.0')
79
+
80
+ requirement += '.a' if bundler_gem_version.prerelease?
81
+
82
+ requirement
83
+ end
84
+
85
+ def load_bundler!
86
+ ENV['BUNDLE_GEMFILE'] ||= gemfile
87
+
88
+ activate_bundler
89
+ end
90
+
91
+ def activate_bundler
92
+ gem_error = activation_error_handling do
93
+ gem 'bundler', bundler_requirement
94
+ end
95
+ return if gem_error.nil?
96
+
97
+ require_error = activation_error_handling do
98
+ require 'bundler/version'
99
+ end
100
+ if require_error.nil? && Gem::Requirement.new(bundler_requirement).satisfied_by?(Gem::Version.new(Bundler::VERSION))
101
+ return
102
+ end
103
+
104
+ warn "Activating bundler (#{bundler_requirement}) failed:\n#{gem_error.message}\n\nTo install the version of bundler this project requires, run `gem install bundler -v '#{bundler_requirement}'`"
105
+ exit 42
106
+ end
107
+
108
+ def activation_error_handling
109
+ yield
110
+ nil
111
+ rescue StandardError, LoadError => e
112
+ e
113
+ end
114
+ end
115
+
116
+ m.load_bundler!
117
+
118
+ load Gem.bin_path('bundler', 'bundle') if m.invoked_as_script?
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require 'bundler/setup'
5
+ require 'simple_images_downloader'
6
+ require 'pry'
7
+
8
+ Pry.start
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env sh
2
+
3
+ set -eu
4
+
5
+ bin/rubocop --config rubocop.yml --parallel
@@ -0,0 +1,29 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ #
5
+ # This file was generated by Bundler.
6
+ #
7
+ # The application 'rspec' is installed as part of a gem, and
8
+ # this file is here to facilitate running it.
9
+ #
10
+
11
+ require 'pathname'
12
+ ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile',
13
+ Pathname.new(__FILE__).realpath)
14
+
15
+ bundle_binstub = File.expand_path('bundle', __dir__)
16
+
17
+ if File.file?(bundle_binstub)
18
+ if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
19
+ load(bundle_binstub)
20
+ else
21
+ abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
22
+ Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
23
+ end
24
+ end
25
+
26
+ require 'rubygems'
27
+ require 'bundler/setup'
28
+
29
+ load Gem.bin_path('rspec-core', 'rspec')
@@ -0,0 +1,29 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ #
5
+ # This file was generated by Bundler.
6
+ #
7
+ # The application 'rubocop' is installed as part of a gem, and
8
+ # this file is here to facilitate running it.
9
+ #
10
+
11
+ require 'pathname'
12
+ ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile',
13
+ Pathname.new(__FILE__).realpath)
14
+
15
+ bundle_binstub = File.expand_path('bundle', __dir__)
16
+
17
+ if File.file?(bundle_binstub)
18
+ if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
19
+ load(bundle_binstub)
20
+ else
21
+ abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
22
+ Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
23
+ end
24
+ end
25
+
26
+ require 'rubygems'
27
+ require 'bundler/setup'
28
+
29
+ load Gem.bin_path('rubocop', 'rubocop')
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ # Make sure we have Bundler installed
7
+ gem install bundler --conservative
8
+
9
+ # Installs all gems
10
+ bundle check --path .bundle > /dev/null 2>&1 || bundle install --jobs=4 --retry=3 --path=.bundle
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require 'simple_images_downloader'
5
+
6
+ SimpleImagesDownloader::Runner.invoke
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'open-uri'
4
+ require 'tempfile'
5
+ require 'zeitwerk'
6
+
7
+ loader = Zeitwerk::Loader.for_gem
8
+ loader.setup
9
+
10
+ module SimpleImagesDownloader
11
+ def self.from_file(path)
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
+
23
+ def self.from_url(url)
24
+ uri = Line.new(url).uri
25
+ Downloader.new(uri).download
26
+ end
27
+
28
+ def self.root
29
+ File.dirname __dir__
30
+ end
31
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'singleton'
4
+
5
+ module SimpleImagesDownloader
6
+ class Configuration
7
+ include Singleton
8
+
9
+ ACCESSORS = %i[
10
+ destination
11
+ ].freeze
12
+
13
+ attr_accessor(*ACCESSORS)
14
+
15
+ def initialize
16
+ @destination = './'
17
+ end
18
+
19
+ def self.configure
20
+ yield instance
21
+ end
22
+
23
+ # rubocop:disable Style/OptionalBooleanParameter
24
+ def self.respond_to_missing?(method, include_private = false)
25
+ accessor?(method) || super
26
+ end
27
+ # rubocop:enable Style/OptionalBooleanParameter
28
+
29
+ def self.method_missing(method, *args)
30
+ super unless accessor?(method.to_s.gsub(/=$/, '').to_sym)
31
+ instance.public_send(method, *args)
32
+ end
33
+
34
+ def self.accessor?(method)
35
+ ACCESSORS.include?(method)
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SimpleImagesDownloader
4
+ class Dispenser
5
+ def initialize(source, remote_path)
6
+ @source = source
7
+ @remote_path = remote_path
8
+ end
9
+
10
+ def place
11
+ raise Errors::DestinationIsNotWritable, destination unless File.writable?(destination_dir)
12
+
13
+ FileUtils.mv @source, destination
14
+ end
15
+
16
+ private
17
+
18
+ def destination
19
+ @destination ||= destination_dir + file_name
20
+ end
21
+
22
+ def file_name
23
+ @file_name ||= File.basename(@source) + File.extname(@remote_path)
24
+ end
25
+
26
+ def destination_dir
27
+ Configuration.destination
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SimpleImagesDownloader
4
+ class Downloader
5
+ REQUEST_OPTIONS = {
6
+ 'User-Agent' => "SimpleImagesDownloader/#{SimpleImagesDownloader::VERSION}",
7
+ redirect: false,
8
+ open_timeout: 30,
9
+ read_timeout: 30
10
+ }.freeze
11
+
12
+ def initialize(uri)
13
+ @uri = uri
14
+ end
15
+
16
+ def download
17
+ puts "Downloading #{@uri}"
18
+
19
+ @downloaded_file = StringioToTempfile.convert(downloaded_file) if downloaded_file.is_a?(StringIO)
20
+
21
+ Dispenser.new(downloaded_file, @uri.path).place
22
+
23
+ puts 'Downloading is finished'
24
+ ensure
25
+ downloaded_file.close
26
+ end
27
+
28
+ private
29
+
30
+ def downloaded_file
31
+ @downloaded_file ||= @uri.open(REQUEST_OPTIONS)
32
+ rescue OpenURI::HTTPRedirect
33
+ raise Errors::RedirectError, @uri
34
+ rescue OpenURI::HTTPError
35
+ raise Errors::ConnectionError, @uri
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SimpleImagesDownloader
4
+ module Errors
5
+ class BaseError < StandardError; end
6
+
7
+ class MissingFileArgumentError < BaseError
8
+ def initialize
9
+ message = 'First arguments must be file'
10
+ super(message)
11
+ end
12
+ end
13
+
14
+ class MissingFileError < BaseError
15
+ def initialize(path)
16
+ message = "File under #{path} path is absent"
17
+ super(message)
18
+ end
19
+ end
20
+
21
+ class BadUrl < BaseError
22
+ def initialize(url)
23
+ message = "The is not valid URL #{url}"
24
+ super(message)
25
+ end
26
+ end
27
+
28
+ class MissingImageInPath < BaseError
29
+ def initialize(path)
30
+ message = "The path doesn't contain image #{path}"
31
+ super(message)
32
+ end
33
+ end
34
+
35
+ class RedirectError < BaseError
36
+ def initialize(uri)
37
+ message = "The url has a redirect #{uri}"
38
+ super(message)
39
+ end
40
+ end
41
+
42
+ class ConnectionError < BaseError
43
+ def initialize(uri)
44
+ message = "There was connection error during downloading file for #{uri}"
45
+ super(message)
46
+ end
47
+ end
48
+
49
+ class DestinationIsNotWritable < BaseError
50
+ def initialize(path)
51
+ message = "The destination is not writable move file manually at #{path}"
52
+ super(message)
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SimpleImagesDownloader
4
+ class FilePersistanceValidator
5
+ def initialize(path)
6
+ @path = path
7
+ end
8
+
9
+ def validate
10
+ raise Errors::MissingFileError, @path unless File.exist?(@path)
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SimpleImagesDownloader
4
+ class ImagePathValidator
5
+ VALID_EXTENSIONS = %w[.png .jpg .gif .jpeg].freeze
6
+
7
+ def initialize(path)
8
+ @path = path
9
+ end
10
+
11
+ def validate
12
+ raise Errors::MissingImageInPath, @path unless VALID_EXTENSIONS.include?(extension)
13
+ end
14
+
15
+ private
16
+
17
+ def extension
18
+ File.extname(@path)
19
+ end
20
+ end
21
+ end