pg_export 0.6.1 → 0.7.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 (79) hide show
  1. checksums.yaml +5 -5
  2. data/.rubocop.yml +9 -13
  3. data/.travis.yml +2 -2
  4. data/CHANGELOG.md +9 -0
  5. data/Gemfile +2 -0
  6. data/README.md +28 -29
  7. data/Rakefile +2 -0
  8. data/bin/console +2 -8
  9. data/bin/pg_export +44 -45
  10. data/lib/pg_export/build_logger.rb +4 -2
  11. data/lib/pg_export/configuration.rb +22 -2
  12. data/lib/pg_export/container.rb +48 -0
  13. data/lib/pg_export/import.rb +7 -0
  14. data/lib/pg_export/lib/pg_export/adapters/bash_adapter.rb +37 -0
  15. data/lib/pg_export/lib/pg_export/adapters/ftp_adapter.rb +67 -0
  16. data/lib/pg_export/lib/pg_export/entities/dump.rb +45 -0
  17. data/lib/pg_export/lib/pg_export/factories/cipher_factory.rb +32 -0
  18. data/lib/pg_export/lib/pg_export/factories/dump_factory.rb +31 -0
  19. data/lib/pg_export/lib/pg_export/factories/ftp_adapter_factory.rb +22 -0
  20. data/lib/pg_export/lib/pg_export/listeners/interactive/build_dump.rb +19 -0
  21. data/lib/pg_export/lib/pg_export/listeners/interactive/close_ftp_connection.rb +19 -0
  22. data/lib/pg_export/lib/pg_export/listeners/interactive/decrypt_dump.rb +19 -0
  23. data/lib/pg_export/lib/pg_export/listeners/interactive/download_dump_from_ftp.rb +19 -0
  24. data/lib/pg_export/lib/pg_export/listeners/interactive/encrypt_dump.rb +19 -0
  25. data/lib/pg_export/lib/pg_export/listeners/interactive/fetch_dumps_from_ftp.rb +19 -0
  26. data/lib/pg_export/lib/pg_export/listeners/interactive/open_ftp_connection.rb +19 -0
  27. data/lib/pg_export/lib/pg_export/listeners/interactive/remove_old_dumps_from_ftp.rb +23 -0
  28. data/lib/pg_export/lib/pg_export/listeners/interactive/restore.rb +19 -0
  29. data/lib/pg_export/lib/pg_export/listeners/interactive/upload_dump_to_ftp.rb +19 -0
  30. data/lib/pg_export/lib/pg_export/listeners/interactive_listener.rb +49 -0
  31. data/lib/pg_export/lib/pg_export/listeners/plain/build_dump.rb +15 -0
  32. data/lib/pg_export/lib/pg_export/listeners/plain/close_ftp_connection.rb +15 -0
  33. data/lib/pg_export/lib/pg_export/listeners/plain/decrypt_dump.rb +15 -0
  34. data/lib/pg_export/lib/pg_export/listeners/plain/download_dump_from_ftp.rb +15 -0
  35. data/lib/pg_export/lib/pg_export/listeners/plain/encrypt_dump.rb +15 -0
  36. data/lib/pg_export/lib/pg_export/listeners/plain/fetch_dumps_from_ftp.rb +15 -0
  37. data/lib/pg_export/lib/pg_export/listeners/plain/open_ftp_connection.rb +15 -0
  38. data/lib/pg_export/lib/pg_export/listeners/plain/remove_old_dumps_from_ftp.rb +17 -0
  39. data/lib/pg_export/lib/pg_export/listeners/plain/restore.rb +15 -0
  40. data/lib/pg_export/lib/pg_export/listeners/plain/upload_dump_to_ftp.rb +15 -0
  41. data/lib/pg_export/lib/pg_export/listeners/plain_listener.rb +17 -0
  42. data/lib/pg_export/lib/pg_export/operations/decrypt_dump.rb +20 -0
  43. data/lib/pg_export/lib/pg_export/operations/encrypt_dump.rb +18 -0
  44. data/lib/pg_export/lib/pg_export/operations/open_ftp_connection.rb +19 -0
  45. data/lib/pg_export/lib/pg_export/operations/remove_old_dumps_from_ftp.rb +22 -0
  46. data/lib/pg_export/lib/pg_export/repositories/ftp_dump_file_repository.rb +19 -0
  47. data/lib/pg_export/lib/pg_export/repositories/ftp_dump_repository.rb +36 -0
  48. data/lib/pg_export/lib/pg_export/transactions/export_dump.rb +56 -0
  49. data/lib/pg_export/lib/pg_export/transactions/import_dump_interactively.rb +67 -0
  50. data/lib/pg_export/lib/pg_export/types.rb +14 -0
  51. data/lib/pg_export/lib/pg_export/ui/interactive/input.rb +36 -0
  52. data/lib/pg_export/lib/pg_export/ui/plain/input.rb +17 -0
  53. data/lib/pg_export/lib/pg_export/value_objects/dump_file.rb +70 -0
  54. data/lib/pg_export/system/boot/config.rb +11 -0
  55. data/lib/pg_export/system/boot/interactive.rb +32 -0
  56. data/lib/pg_export/system/boot/logger.rb +15 -0
  57. data/lib/pg_export/system/boot/plain.rb +33 -0
  58. data/lib/pg_export/version.rb +3 -1
  59. data/lib/pg_export.rb +23 -20
  60. data/pg_export.gemspec +13 -10
  61. metadata +108 -52
  62. data/lib/pg_export/aes/base.rb +0 -47
  63. data/lib/pg_export/aes/decryptor.rb +0 -13
  64. data/lib/pg_export/aes/encryptor.rb +0 -13
  65. data/lib/pg_export/aes.rb +0 -3
  66. data/lib/pg_export/bash/adapter.rb +0 -31
  67. data/lib/pg_export/bash/factory.rb +0 -23
  68. data/lib/pg_export/bash/repository.rb +0 -18
  69. data/lib/pg_export/boot_container.rb +0 -69
  70. data/lib/pg_export/dump.rb +0 -62
  71. data/lib/pg_export/errors.rb +0 -5
  72. data/lib/pg_export/ftp/adapter.rb +0 -41
  73. data/lib/pg_export/ftp/connection.rb +0 -40
  74. data/lib/pg_export/ftp/repository.rb +0 -40
  75. data/lib/pg_export/roles/colourable_string.rb +0 -19
  76. data/lib/pg_export/roles/human_readable.rb +0 -17
  77. data/lib/pg_export/roles/interactive.rb +0 -98
  78. data/lib/pg_export/roles/validatable.rb +0 -24
  79. data/lib/pg_export/services/create_and_export_dump.rb +0 -20
@@ -0,0 +1,67 @@
1
+ # frozen_string_literal: true
2
+
3
+ # auto_register: false
4
+
5
+ require 'dry/transaction'
6
+
7
+ require 'pg_export/import'
8
+ require 'pg_export/container'
9
+
10
+ class PgExport
11
+ module Transactions
12
+ class ImportDumpInteractively
13
+ include Dry::Transaction(container: PgExport::Container)
14
+ include Import[
15
+ 'adapters.bash_adapter',
16
+ 'repositories.ftp_dump_repository',
17
+ 'repositories.ftp_dump_file_repository',
18
+ 'ui_input'
19
+ ]
20
+
21
+ step :open_ftp_connection, with: 'operations.open_ftp_connection'
22
+ step :fetch_dumps_from_ftp
23
+ step :select_dump
24
+ step :download_dump_from_ftp
25
+ step :close_ftp_connection
26
+ step :decrypt_dump, with: 'operations.decrypt_dump'
27
+ step :select_database
28
+ step :restore
29
+
30
+ private
31
+
32
+ def fetch_dumps_from_ftp(database_name:, ftp_adapter:)
33
+ dumps = ftp_dump_repository.all(database_name: database_name, ftp_adapter: ftp_adapter)
34
+ return Failure(message: 'No dumps') if dumps.none?
35
+
36
+ Success(ftp_adapter: ftp_adapter, dumps: dumps)
37
+ end
38
+
39
+ def select_dump(dumps:, ftp_adapter:)
40
+ dump = ui_input.select_dump(dumps)
41
+ Success(dump: dump, ftp_adapter: ftp_adapter)
42
+ end
43
+
44
+ def download_dump_from_ftp(dump:, ftp_adapter:)
45
+ dump.file = ftp_dump_file_repository.by_name(name: dump.name, ftp_adapter: ftp_adapter)
46
+ Success(dump: dump, ftp_adapter: ftp_adapter)
47
+ end
48
+
49
+ def close_ftp_connection(dump:, ftp_adapter:)
50
+ Thread.new { ftp_adapter.close_ftp }
51
+ Success(dump: dump)
52
+ end
53
+
54
+ def select_database(dump:)
55
+ name = ui_input.enter_database_name(dump.database)
56
+ Success(dump: dump, database: name)
57
+ end
58
+
59
+ def restore(dump:, database:)
60
+ bash_adapter.pg_restore(dump.file, database)
61
+ Success({})
62
+ rescue bash_adapter.class::PgRestoreError => e
63
+ Failure(message: e.to_s)
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'dry-types'
4
+ require 'pg_export/lib/pg_export/value_objects/dump_file'
5
+
6
+ class PgExport
7
+ module Types
8
+ include Dry::Types.module
9
+
10
+ DumpName = Strict::String.constrained(format: /.+_20[0-9]{6}_[0-9]{6}\Z/)
11
+ DumpType = Types::Coercible::String.enum('plain', 'encrypted')
12
+ DumpFile = Types.Instance(PgExport::ValueObjects::DumpFile)
13
+ end
14
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'tty-prompt'
4
+
5
+ class PgExport
6
+ module Ui
7
+ module Interactive
8
+ class Input
9
+ def select_dump(dumps)
10
+ idx = prompt.select('Select dump to import:') do |menu|
11
+ menu.enum '.'
12
+ dumps.each_with_index do |d, i|
13
+ menu.choice(d.to_s, i)
14
+ end
15
+ end
16
+
17
+ dumps[idx]
18
+ end
19
+
20
+ def enter_database_name(default = nil)
21
+ puts 'To which database would you like to restore the downloaded dump?'
22
+ prompt.ask('Enter a local database name:') do |q|
23
+ q.required(true)
24
+ q.default(default) if default
25
+ end
26
+ end
27
+
28
+ private
29
+
30
+ def prompt
31
+ TTY::Prompt.new
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ class PgExport
4
+ module Ui
5
+ module Plain
6
+ class Input
7
+ def select_dump(dumps)
8
+ dumps[0]
9
+ end
10
+
11
+ def enter_database_name(default)
12
+ default
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,70 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'tempfile'
4
+
5
+ class PgExport
6
+ module ValueObjects
7
+ class DumpFile
8
+ def initialize(file = Tempfile.new)
9
+ @file = file
10
+ end
11
+
12
+ def path
13
+ file.path
14
+ end
15
+
16
+ def size
17
+ file.size
18
+ end
19
+
20
+ def rewind
21
+ file.rewind
22
+ end
23
+
24
+ def read
25
+ file.read
26
+ end
27
+
28
+ def copy(cipher:)
29
+ cipher.reset
30
+ new_self = self.class.new
31
+ new_self.write do |f|
32
+ each_chunk do |chunk|
33
+ f << cipher.update(chunk)
34
+ end
35
+ f << cipher.final
36
+ end
37
+
38
+ new_self
39
+ end
40
+
41
+ def write(&block)
42
+ File.open(path, 'w', &block)
43
+ end
44
+
45
+ def each_chunk
46
+ File.open(path, 'r') do |file|
47
+ yield file.read(CHUNK_SIZE) until file.eof?
48
+ end
49
+ end
50
+
51
+ def size_human
52
+ MAPPING.each_pair { |e, s| return "#{(size.to_f / (s / 1024)).round(2)}#{e}" if size < s }
53
+ end
54
+
55
+ private
56
+
57
+ CHUNK_SIZE = (2**16).freeze
58
+ MAPPING = {
59
+ 'B' => 1024,
60
+ 'kB' => 1024 * 1024,
61
+ 'MB' => 1024 * 1024 * 1024,
62
+ 'GB' => 1024 * 1024 * 1024 * 1024,
63
+ 'TB' => 1024 * 1024 * 1024 * 1024 * 1024
64
+ }.freeze
65
+ private_constant :CHUNK_SIZE, :MAPPING
66
+
67
+ attr_reader :file
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ PgExport::Container.boot :config do
4
+ init do
5
+ require 'pg_export/configuration'
6
+ end
7
+
8
+ start do
9
+ register(:config, memoize: true) { PgExport::Configuration.build(ENV) }
10
+ end
11
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ PgExport::Container.boot(:interactive) do
4
+ init do
5
+ require 'pg_export/lib/pg_export/transactions/import_dump_interactively'
6
+ end
7
+
8
+ start do
9
+ use :main
10
+
11
+ # type = 'plain'
12
+ type = 'interactive'
13
+
14
+ transaction = PgExport::Transactions::ImportDumpInteractively.new(ui_input: target["ui.#{type}.input"])
15
+
16
+ unless target[:config].logger_muted?
17
+ use :logger
18
+
19
+ %i[
20
+ open_ftp_connection
21
+ fetch_dumps_from_ftp
22
+ download_dump_from_ftp
23
+ decrypt_dump
24
+ restore
25
+ ].each do |step|
26
+ transaction.subscribe(step => target["listeners.#{type}.#{step}"])
27
+ end
28
+ end
29
+
30
+ register('transactions.import_dump_interactively', memoize: true) { transaction }
31
+ end
32
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ PgExport::Container.boot :logger do
4
+ init do
5
+ require 'pg_export/build_logger'
6
+ end
7
+
8
+ start do
9
+ use :config
10
+
11
+ register(:logger, memoize: true) do
12
+ PgExport::BuildLogger.call(stream: $stdout, format: target[:config].logger_format)
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ PgExport::Container.boot(:plain) do
4
+ init do
5
+ require 'pg_export/lib/pg_export/transactions/export_dump'
6
+ end
7
+
8
+ start do
9
+ use :main
10
+
11
+ transaction = PgExport::Transactions::ExportDump.new
12
+
13
+ unless target[:config].logger_muted?
14
+ use :logger
15
+
16
+ type = 'plain'
17
+ # type = 'interactive'
18
+
19
+ %i[
20
+ build_dump
21
+ encrypt_dump
22
+ open_ftp_connection
23
+ upload_dump_to_ftp
24
+ remove_old_dumps_from_ftp
25
+ close_ftp_connection
26
+ ].each do |step|
27
+ transaction.subscribe(step => target["listeners.#{type}.#{step}"])
28
+ end
29
+ end
30
+
31
+ register('transactions.export_dump', memoize: true) { transaction }
32
+ end
33
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class PgExport
2
- VERSION = '0.6.1'.freeze
4
+ VERSION = '0.7.0'
3
5
  end
data/lib/pg_export.rb CHANGED
@@ -1,29 +1,32 @@
1
- require 'pg_export/version'
2
- require 'pg_export/configuration'
3
- require 'pg_export/boot_container'
4
- require 'pg_export/roles/interactive'
5
- require 'pg_export/errors'
6
- require 'pg_export/roles/validatable'
1
+ # frozen_string_literal: true
2
+
3
+ require 'pry'
4
+ require 'pg_export/container'
7
5
 
8
6
  class PgExport
9
- include Roles::Validatable
10
-
11
- def initialize(**args)
12
- config = Configuration.new(**args)
13
- extend Roles::Interactive if config.interactive
14
- @container = BootContainer.call(config.to_h)
15
- rescue Dry::Struct::Error => e
16
- raise ArgumentError, e
7
+ class InitializationError < StandardError; end
8
+
9
+ class << self
10
+ def interactive
11
+ PgExport::Container.start(:interactive)
12
+ new(transaction: PgExport::Container['transactions.import_dump_interactively'])
13
+ end
14
+
15
+ def plain
16
+ PgExport::Container.start(:plain)
17
+ new(transaction: PgExport::Container['transactions.export_dump'])
18
+ end
19
+ end
20
+
21
+ def initialize(transaction:)
22
+ @transaction = transaction
17
23
  end
18
24
 
19
- def call(database_name, keep_dumps)
20
- container[:create_and_export_dump].call(
21
- validate_database_name(database_name),
22
- validate_keep_dumps(keep_dumps)
23
- )
25
+ def call(database_name, &block)
26
+ transaction.call(database_name: database_name, &block)
24
27
  end
25
28
 
26
29
  private
27
30
 
28
- attr_reader :container
31
+ attr_reader :transaction
29
32
  end
data/pg_export.gemspec CHANGED
@@ -1,5 +1,6 @@
1
- # coding: utf-8
2
- lib = File.expand_path('../lib', __FILE__)
1
+ # frozen_string_literal: true
2
+
3
+ lib = File.expand_path('lib', __dir__)
3
4
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
5
  require 'pg_export/version'
5
6
 
@@ -18,16 +19,18 @@ Gem::Specification.new do |spec|
18
19
  spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
19
20
  spec.executables = ['pg_export']
20
21
  spec.require_paths = ['lib']
21
- spec.required_ruby_version = '>= 2.2.0'
22
+ spec.required_ruby_version = '>= 2.3.0'
22
23
 
23
- spec.add_dependency 'cli_spinnable', '~> 0.2'
24
- spec.add_dependency 'dry-types', '~> 0.11.1'
25
- spec.add_dependency 'dry-struct', '~> 0.3.1'
24
+ spec.add_dependency 'dry-initializer', '~> 2.5.0'
25
+ spec.add_dependency 'dry-system', '~> 0.10.0'
26
+ spec.add_dependency 'dry-transaction', '~> 0.13.0'
27
+ spec.add_dependency 'tty-prompt'
28
+ spec.add_dependency 'tty-spinner'
26
29
 
27
- spec.add_development_dependency 'bundler', '~> 1.10'
28
- spec.add_development_dependency 'rubocop', '~> 0.44'
30
+ spec.add_development_dependency 'bundler', '~> 1.16'
31
+ spec.add_development_dependency 'pg', '~> 0.21'
32
+ spec.add_development_dependency 'pry'
29
33
  spec.add_development_dependency 'rake', '~> 10.0'
30
34
  spec.add_development_dependency 'rspec', '~> 3.4'
31
- spec.add_development_dependency 'pg', '~> 0.19'
32
- spec.add_development_dependency 'pry'
35
+ spec.add_development_dependency 'rubocop', '~> 0.59.2'
33
36
  end