database_patcher 1.3.0 → 1.4.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
  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