database_patcher 1.3.0 → 1.4.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: a6f30ae5f966a66d023dedb2e52ec85352fcc2dc
4
- data.tar.gz: f2dad5348b3d64fbf9e2655fd8c26bdb93a44db6
3
+ metadata.gz: 4dd4e79091a8bbff51742008dbe13648634ec1e9
4
+ data.tar.gz: 0a105b04e9c13b5abe5bff2cd9e8a6e139ed9ddd
5
5
  SHA512:
6
- metadata.gz: f0262661418ce2071f9518ac20cef05d185f2b378a4a2af4da734cf02f5692088b3e3eed5bd6f4135351c6ba7af94279063066ed42bba86f1fec90979f3c4301
7
- data.tar.gz: 7c4999481222febae37e67fa6f0c7b7bea7c2c8b75ed9b53c0931c99a0e0b4b0d3753b2f92653fc8425c8cb0c0e40175158d56423f2e2ddd4b1e0d7baee97336
6
+ metadata.gz: 79d02146d5dc66f9ce04d29cdeb580fe110dde9946c8e691dbb75494e7d9314c94c0d50960fc2953d8b785f9ff56d743987b7397a4e8cfab315129149834e3e3
7
+ data.tar.gz: 1ff2bce215c6020902fbb6eab6a33a9a5369f07bee0bc25335596a5387f562ed8d145745593f3efa795994110d8502b13858fe65ee3b144d9a958ce129aac6dc
@@ -2,12 +2,11 @@ require 'fileutils'
2
2
  module DatabasePatcher
3
3
  require 'database_patcher/db'
4
4
  require 'database_patcher/cli'
5
+ require 'database_patcher/action'
5
6
  require 'database_patcher/fetcher'
6
7
  require 'database_patcher/version'
7
8
  require 'database_patcher/command'
9
+ require 'database_patcher/interface'
8
10
  require 'database_patcher/environment'
9
- require 'database_patcher/initializer'
10
11
  require 'database_patcher/patch_entity'
11
- require 'database_patcher/patch_creator'
12
- require 'database_patcher/patch_applier'
13
12
  end
@@ -0,0 +1,11 @@
1
+ require 'database_patcher'
2
+ class DatabasePatcher::Action
3
+ require 'database_patcher/action/initializer'
4
+ require 'database_patcher/action/patch_applier'
5
+ require 'database_patcher/action/patch_creator'
6
+
7
+ attr_reader :interface
8
+ def initialize(interface=DatabasePatcher::Interface::Null.new)
9
+ @interface = interface
10
+ end
11
+ end
@@ -0,0 +1,49 @@
1
+ require 'database_patcher'
2
+ class DatabasePatcher::Action::Initializer < DatabasePatcher::Action
3
+ def init
4
+ create_patch_folder
5
+ check_table_exists
6
+ check_required_columns
7
+ rescue Sequel::DatabaseError => explanation
8
+ connection.create_table(:installed_patches) do
9
+ Integer :timestamp
10
+ String :uuid, size: 36
11
+ String :md5_down, size: 32
12
+ String :md5_up, size: 32
13
+ String :comment
14
+ end
15
+ end
16
+
17
+ protected
18
+
19
+ def check_required_columns
20
+ test_column(:timestamp, Integer)
21
+ test_column(:uuid, String, size: 36, default: "")
22
+ test_column(:md5_down, String, size: 32)
23
+ test_column(:md5_up, String, size: 32)
24
+ test_column(:md5_up, String, size: 32)
25
+ test_column(:comment, String)
26
+ end
27
+
28
+ def test_column(name, type, opts = {})
29
+ connection.transaction do
30
+ unless connection[:installed_patches].columns.include?(name)
31
+ connection.alter_table(:installed_patches) do
32
+ add_column(name, type, opts)
33
+ end
34
+ end
35
+ end
36
+ end
37
+
38
+ def check_table_exists
39
+ connection[:installed_patches].first
40
+ end
41
+
42
+ def create_patch_folder
43
+ FileUtils.mkpath(DatabasePatcher::Environment.patch_folder_path)
44
+ end
45
+
46
+ def connection
47
+ @connection ||= DatabasePatcher::DB.create_connection
48
+ end
49
+ end
@@ -1,9 +1,9 @@
1
1
  require 'database_patcher'
2
- class DatabasePatcher::PatchApplier
2
+ class DatabasePatcher::Action::PatchApplier < DatabasePatcher::Action
3
3
  def up
4
4
  connection = DatabasePatcher::DB.create_connection
5
5
  connection.transaction do
6
- fetcher = DatabasePatcher::Fetcher.new(connection)
6
+ fetcher = DatabasePatcher::Fetcher.new(connection, interface)
7
7
  fetcher.get_pending_patches.each do |pending_patch|
8
8
  pending_patch.up(connection)
9
9
  end
@@ -11,9 +11,10 @@ class DatabasePatcher::PatchApplier
11
11
  end
12
12
 
13
13
  def down
14
+ return unless interface.ask('This will execute all the down patches! Are you sure?')
14
15
  connection = DatabasePatcher::DB.create_connection
15
16
  connection.transaction do
16
- fetcher = DatabasePatcher::Fetcher.new(connection)
17
+ fetcher = DatabasePatcher::Fetcher.new(connection, interface)
17
18
  fetcher.get_intalled_patches.each do |installed_patche|
18
19
  installed_patche.down(connection)
19
20
  end
@@ -21,9 +22,10 @@ class DatabasePatcher::PatchApplier
21
22
  end
22
23
 
23
24
  def rollback
25
+ return unless interface.ask('This will execute the last patch down part! Are you sure?')
24
26
  connection = DatabasePatcher::DB.create_connection
25
27
  connection.transaction do
26
- fetcher = DatabasePatcher::Fetcher.new(connection)
28
+ fetcher = DatabasePatcher::Fetcher.new(connection, interface)
27
29
  fetcher.get_intalled_patches.each do |patch|
28
30
  patch.down(connection)
29
31
  break
@@ -1,7 +1,8 @@
1
1
  require 'fileutils'
2
2
  require 'database_patcher'
3
- class DatabasePatcher::PatchCreator
4
- def initialize(type, idenpotent, file_name_description)
3
+ class DatabasePatcher::Action::PatchCreator < DatabasePatcher::Action
4
+ def initialize(type, idenpotent, file_name_description, interface = DatabasePatcher::Interface::Null)
5
+ super(interface)
5
6
  @type = type
6
7
  @idenpotent = !!idenpotent
7
8
  @file_name_description = file_name_description
@@ -15,6 +15,7 @@ class DatabasePatcher::Command::CreatePatch < DatabasePatcher::Command
15
15
  end
16
16
 
17
17
  on_call do |args|
18
- DatabasePatcher::PatchCreator.new(options[:type], options[:idempotent],args.join('_')).make
18
+ std = DatabasePatcher::Interface::STD.new
19
+ DatabasePatcher::Action::PatchCreator.new(options[:type], options[:idempotent],args.join('_'), std).make
19
20
  end
20
21
  end
@@ -4,7 +4,8 @@ class DatabasePatcher::Command::Down < DatabasePatcher::Command
4
4
  desc 'execute the down patches and remove all db patch'
5
5
 
6
6
  on_call do |*_|
7
- DatabasePatcher::Initializer.new.init
8
- DatabasePatcher::PatchApplier.new.down
7
+ std = DatabasePatcher::Interface::STD.new
8
+ DatabasePatcher::Action::Initializer.new(std).init
9
+ DatabasePatcher::Action::PatchApplier.new(std).down
9
10
  end
10
11
  end
@@ -4,6 +4,7 @@ class DatabasePatcher::Command::Init < DatabasePatcher::Command
4
4
  desc 'this will create initial directory and the default installed_patches table in the database'
5
5
 
6
6
  on_call do |*_|
7
- DatabasePatcher::Initializer.new.init
7
+ std = DatabasePatcher::Interface::STD.new
8
+ DatabasePatcher::Action::Initializer.new(std).init
8
9
  end
9
10
  end
@@ -4,7 +4,8 @@ class DatabasePatcher::Command::RollBack < DatabasePatcher::Command
4
4
  desc 'execute the last patch down part, and remove the db patch registration'
5
5
 
6
6
  on_call do |*_|
7
- DatabasePatcher::Initializer.new.init
8
- DatabasePatcher::PatchApplier.new.rollback
7
+ std = DatabasePatcher::Interface::STD.new
8
+ DatabasePatcher::Action::Initializer.new(std).init
9
+ DatabasePatcher::Action::PatchApplier.new(std).rollback
9
10
  end
10
11
  end
@@ -4,7 +4,8 @@ class DatabasePatcher::Command::Up < DatabasePatcher::Command
4
4
  desc 'apply all pending db patch'
5
5
 
6
6
  on_call do |*_|
7
- DatabasePatcher::Initializer.new.init
8
- DatabasePatcher::PatchApplier.new.up
7
+ std = DatabasePatcher::Interface::STD.new
8
+ DatabasePatcher::Action::Initializer.new(std).init
9
+ DatabasePatcher::Action::PatchApplier.new(std).up
9
10
  end
10
11
  end
@@ -7,14 +7,13 @@ module DatabasePatcher::DB
7
7
 
8
8
  def create_connection
9
9
  new_connection = Sequel.connect(DatabasePatcher::Environment.database_url)
10
- new_connection.sql_log_level = :info
11
10
  new_connection.loggers << new_logger
12
11
  new_connection
13
12
  end
14
13
 
15
14
  def new_logger
16
15
  logger = Logger.new($stdout)
17
- logger.level = Logger::Severity::ERROR
16
+ logger.level = Logger::Severity::UNKNOWN
18
17
  logger
19
18
  end
20
19
 
@@ -1,29 +1,30 @@
1
1
  require 'database_patcher'
2
2
  class DatabasePatcher::Fetcher
3
- def initialize(connection)
3
+ def initialize(connection, interface)
4
4
  @connection = connection
5
+ @interface = interface
5
6
  end
6
7
 
7
8
  def get_intalled_patches
8
- patches = get_patches.reverse
9
+ patches = get_patches
9
10
  installed_patches = []
10
- already_applied_patch_timestamps = get_already_applied_patch_timestamps
11
+ uniq_indentifiers = get_already_applied_patch_uniq_indentifiers
11
12
 
12
13
  patches.each do |patch|
13
- break unless already_applied_patch_timestamps.include?(patch.timestamp)
14
+ break unless uniq_indentifiers.include?(patch.uniq_indentifier)
14
15
  installed_patches.push(patch)
15
16
  end
16
17
 
17
- installed_patches
18
+ installed_patches.reverse
18
19
  end
19
20
 
20
21
  def get_pending_patches
21
22
  patches = get_patches
22
23
  pending_patches = []
23
- already_applied_patch_timestamps = get_already_applied_patch_timestamps
24
+ uniq_indentifiers = get_already_applied_patch_uniq_indentifiers
24
25
 
25
26
  patches.each do |patch|
26
- break if already_applied_patch_timestamps.include?(patch.timestamp)
27
+ break if uniq_indentifiers.include?(patch.uniq_indentifier)
27
28
  pending_patches.push(patch)
28
29
  end
29
30
 
@@ -36,7 +37,7 @@ class DatabasePatcher::Fetcher
36
37
 
37
38
  def get_patches
38
39
  Dir.glob(File.join(DatabasePatcher::Environment.patch_folder_path, '*')).reduce([]) do |patches, current_path|
39
- patches << DatabasePatcher::PatchEntity.factory(current_path)
40
+ patches << DatabasePatcher::PatchEntity.factory(current_path, @interface)
40
41
  patches
41
42
  end.sort_by(&:timestamp)
42
43
  end
@@ -45,7 +46,12 @@ class DatabasePatcher::Fetcher
45
46
  connection[:installed_patches].all
46
47
  end
47
48
 
48
- def get_already_applied_patch_timestamps
49
- installed_patches.map { |record| record[:timestamp] }
49
+ def get_already_applied_patch_uniq_indentifiers
50
+ installed_patches.map do |record|
51
+ {
52
+ timestamp: record[:timestamp],
53
+ uuid: record[:uuid]
54
+ }
55
+ end.sort_by{|r| r[:timestamp].to_i }
50
56
  end
51
57
  end
@@ -0,0 +1,9 @@
1
+ require 'database_patcher'
2
+ class DatabasePatcher::Interface
3
+ require 'database_patcher/interface/null'
4
+ require 'database_patcher/interface/std'
5
+
6
+ def ask(_question)
7
+ raise(NotImplementedError)
8
+ end
9
+ end
@@ -0,0 +1,6 @@
1
+ require 'database_patcher'
2
+ class DatabasePatcher::Interface::Null < DatabasePatcher::Interface
3
+ def ask(_q)
4
+ true
5
+ end
6
+ end
@@ -0,0 +1,24 @@
1
+ require 'timeout'
2
+ require 'database_patcher'
3
+ class DatabasePatcher::Interface::STD < DatabasePatcher::Interface
4
+ def initialize(options = {})
5
+ @force = !!options[:force]
6
+ end
7
+
8
+ def ask(question)
9
+ return true if @force
10
+ Timeout.timeout(15) do
11
+ loop do
12
+ $stdout.puts(question.to_s + ' ([y]es/[n]o)')
13
+ case $stdin.gets.chomp.strip.downcase
14
+ when 'y', 'yes'
15
+ return true
16
+ when 'n', 'no'
17
+ return false
18
+ else
19
+ $stdout.puts('invalid answer, please try again')
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -4,23 +4,30 @@ class DatabasePatcher::PatchEntity
4
4
  require 'database_patcher/patch_entity/file'
5
5
  require 'database_patcher/patch_entity/folder'
6
6
 
7
- def self.factory(path)
7
+ def self.factory(path, interface)
8
8
  if ::File.directory?(path)
9
- self::Folder.new(path)
9
+ self::Folder.new(path, interface)
10
10
  else
11
- self::File.new(path)
11
+ self::File.new(path, interface)
12
12
  end
13
13
  end
14
14
 
15
- def initialize(path)
15
+ attr_reader :path, :interface
16
+ def initialize(path, interface)
16
17
  @path = path
18
+ @interface = interface
17
19
  end
18
20
 
19
21
  def timestamp
20
- basename = ::File.basename(@path, '.*')
21
22
  basename.scan(/^\d+/).flatten.first.to_i
22
23
  end
23
24
 
25
+ UUID_MATCHER = '[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}'.freeze
26
+ def uuid
27
+ basename = ::File.basename(@path, '.*')
28
+ basename.scan(/^\d+_(#{UUID_MATCHER})_/).flatten.first.to_s
29
+ end
30
+
24
31
  def up(_connection)
25
32
  raise
26
33
  end
@@ -29,6 +36,15 @@ class DatabasePatcher::PatchEntity
29
36
  raise
30
37
  end
31
38
 
39
+ def uniq_indentifier
40
+ {
41
+ timestamp: timestamp,
42
+ uuid: uuid
43
+ }
44
+ end
45
+
46
+ protected
47
+
32
48
  def execute_file(connection, path)
33
49
  case ::File.extname(path)
34
50
 
@@ -54,16 +70,12 @@ class DatabasePatcher::PatchEntity
54
70
  $stdout.puts("Patch removed: #{uniq_indentifier[:timestamp]}")
55
71
  end
56
72
 
57
- def uniq_indentifier
73
+ def patch_record
58
74
  {
59
- timestamp: timestamp,
60
75
  md5_down: md5_down,
61
- md5_up: md5_up
62
- }
63
- end
64
-
65
- def patch_record
66
- uniq_indentifier.merge(comment: comment)
76
+ md5_up: md5_up,
77
+ comment: comment
78
+ }.merge(uniq_indentifier)
67
79
  end
68
80
 
69
81
  def md5(string)
@@ -95,4 +107,8 @@ class DatabasePatcher::PatchEntity
95
107
  def raise_unknown_extension_for(path)
96
108
  raise("unknown patch file extension: #{::File.extname(path)} (#{path})")
97
109
  end
110
+
111
+ def basename
112
+ ::File.basename(@path, '.*')
113
+ end
98
114
  end
@@ -1,31 +1,29 @@
1
1
  require "database_patcher"
2
- namespace :db do
2
+ std = DatabasePatcher::Interface::STD.new
3
+ namespace :dp do
3
4
 
4
5
  desc 'apply pending schema migrations'
5
- task :apply_pending_patches do
6
- DatabasePatcher::Initializer.new.init
7
- DatabasePatcher::PatchApplier.new.up
6
+ task :up do
7
+ DatabasePatcher::Action::Initializer.new(std).init
8
+ DatabasePatcher::Action::PatchApplier.new(std).up
8
9
  end
9
10
 
10
- desc "Alias task for apply_pending_patches"
11
- task :migrate => :apply_pending_patches
12
-
13
11
  desc 'apply pending schema migrations'
14
- task :revert_installed_patches do
15
- DatabasePatcher::Initializer.new.init
16
- DatabasePatcher::PatchApplier.new.down
12
+ task :down do
13
+ DatabasePatcher::Action::Initializer.new(std).init
14
+ DatabasePatcher::Action::PatchApplier.new(std).down
17
15
  end
18
16
 
19
17
  desc 'rollback one patch'
20
18
  task :rollback do
21
- DatabasePatcher::Initializer.new.init
22
- DatabasePatcher::PatchApplier.new.rollback
19
+ DatabasePatcher::Action::Initializer.new(std).init
20
+ DatabasePatcher::Action::PatchApplier.new(std).rollback
23
21
  end
24
22
 
25
23
  desc 'create a new migration patch'
26
- task :create_patch, :type, :idempotent, :description do |t, args|
24
+ task :add, :type, :idempotent, :description do |t, args|
27
25
  args.with_defaults(:type => 'ruby', :idempotent => false, :description => '')
28
- DatabasePatcher::PatchCreator.new(args[:type], args[:idempotent],args[:description]).make
26
+ DatabasePatcher::Action::PatchCreator.new(args[:type], args[:idempotent],args[:description],std).make
29
27
  end
30
28
 
31
29
  end
@@ -1,3 +1,3 @@
1
1
  module DatabasePatcher
2
- VERSION = "1.3.0"
2
+ VERSION = "1.4.0"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: database_patcher
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.3.0
4
+ version: 1.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Adam Luzsi
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2016-05-26 00:00:00.000000000 Z
11
+ date: 2016-05-31 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -104,7 +104,6 @@ extra_rdoc_files: []
104
104
  files:
105
105
  - ".env"
106
106
  - ".gitignore"
107
- - ".rspec"
108
107
  - ".travis.yml"
109
108
  - CODE_OF_CONDUCT.md
110
109
  - Gemfile
@@ -115,6 +114,10 @@ files:
115
114
  - database_patcher.gemspec
116
115
  - exe/database_patcher
117
116
  - lib/database_patcher.rb
117
+ - lib/database_patcher/action.rb
118
+ - lib/database_patcher/action/initializer.rb
119
+ - lib/database_patcher/action/patch_applier.rb
120
+ - lib/database_patcher/action/patch_creator.rb
118
121
  - lib/database_patcher/cli.rb
119
122
  - lib/database_patcher/command.rb
120
123
  - lib/database_patcher/command/create_patch.rb
@@ -126,9 +129,9 @@ files:
126
129
  - lib/database_patcher/db.rb
127
130
  - lib/database_patcher/environment.rb
128
131
  - lib/database_patcher/fetcher.rb
129
- - lib/database_patcher/initializer.rb
130
- - lib/database_patcher/patch_applier.rb
131
- - lib/database_patcher/patch_creator.rb
132
+ - lib/database_patcher/interface.rb
133
+ - lib/database_patcher/interface/null.rb
134
+ - lib/database_patcher/interface/std.rb
132
135
  - lib/database_patcher/patch_entity.rb
133
136
  - lib/database_patcher/patch_entity/file.rb
134
137
  - lib/database_patcher/patch_entity/folder.rb
data/.rspec DELETED
@@ -1,2 +0,0 @@
1
- --format documentation
2
- --color
@@ -1,28 +0,0 @@
1
- require 'database_patcher'
2
- class DatabasePatcher::Initializer
3
- def init
4
- create_patch_folder
5
- check_table_exists
6
- rescue Sequel::DatabaseError
7
- connection.create_table(:installed_patches) do
8
- Integer :timestamp
9
- String :md5_down, size: 32
10
- String :md5_up, size: 32
11
- String :comment
12
- end
13
- end
14
-
15
- protected
16
-
17
- def check_table_exists
18
- connection[:installed_patches].first
19
- end
20
-
21
- def create_patch_folder
22
- FileUtils.mkpath(DatabasePatcher::Environment.patch_folder_path)
23
- end
24
-
25
- def connection
26
- @connection ||= DatabasePatcher::DB.create_connection
27
- end
28
- end