cfgdatabase 1.2.4 → 1.2.5

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
  SHA256:
3
- metadata.gz: 2944c3a5397dbac2ff52e00e1c7f1ddd97f020d39163c6b8782102f704ee59c0
4
- data.tar.gz: 4a290291434d2b6e2af52d94f342a28f56cd67447aac83f4c3905cf081dc2b8c
3
+ metadata.gz: 606c2258083d60aa81e7d114894d9b22fff92c33036981ce1053371a2897607b
4
+ data.tar.gz: 3dd8a996e9a7a233eb698bc2ae93b25aafabbf0bac0f156650daac3e911dd3da
5
5
  SHA512:
6
- metadata.gz: 64de309e76937c5bb812c32c97dccc239c7c19bc64b5bd2643d62da7345b5aafe18466de8b0138258bc058975ec17ed54830a2a911ea223ab973990c00fb81d5
7
- data.tar.gz: 97d304a78602270719b7e7240d41fc5d2d652bfa2d1f1184ff73957e6fc4edba27a329b4bb7fe3d0f585352b5139fcdd126e933223fbe35a956643f8908fb5df
6
+ metadata.gz: 7f271b2a238c361ee4e497b0bcde8dc6650ea2aa370c266f906dd225cf67b8b79f5df168e294baae2dec22ddb5dfb27d52b04232bc3cd24b2afc79f86e0e7c38
7
+ data.tar.gz: b444cc1b1bb12fbb7ee69f8d18ab820e95fe5dda08ea8a1e6316e46b09688ab776526fc9c8ca6333e55de125b7ac33b5d292f8431ad33873a454c9cdbe848dca
data/utils/migrate.rb ADDED
@@ -0,0 +1,186 @@
1
+ #!/usr/bin/env ruby
2
+ # encoding: utf-8
3
+
4
+ require 'bundler/setup'
5
+ require 'fileutils'
6
+ require 'pathname'
7
+ require 'bunny'
8
+ require 'logger'
9
+ require 'yaml'
10
+ require 'optparse'
11
+ require 'thor'
12
+ require 'monkey-hash'
13
+ require 'app-config'
14
+ require 'app-logger'
15
+ require 'sequel'
16
+ require 'pg'
17
+ require 'app-database'
18
+
19
+ App::Config.init( approot: Pathname.new( Dir.pwd ).expand_path )
20
+ App::Logger.new
21
+ App::Database.init if Cfg.db?
22
+ Sequel.extension :migration
23
+
24
+ # тупой способ определения запуска в корне проекта
25
+ launchdir = Dir.pwd
26
+ if launchdir != Cfg.root || ! File.exist?( "#{ Cfg.root }/Gemfile" )
27
+ puts "\n\tВнимание! Следует запускать migrate.rb в корне проекта.\n\n"
28
+ raise "\n\tВнимание! Следует запускать migrate.rb в корне проекта.\n\n"
29
+ end
30
+
31
+ class Dbtask < Thor
32
+ package_name 'db'
33
+ desc 'init', 'Создать папки и записать нулевую миграцию'
34
+ def init
35
+ mydir = Pathname( __dir__ ).expand_path.to_s
36
+ migrations = Pathname( "#{ Cfg.root }/db/migrations" ).expand_path.to_s
37
+ Log.info{"Создаю папку #{ migrations }"}
38
+ FileUtils.mkdir_p migrations
39
+ list = Dir[ "#{ mydir }/migrations/*rb" ]
40
+ Log.info{"Копирую файл#{ list.count == 1 ? '' : 'ы' } с миграциями:\n#{ list.join("\n") }"}
41
+ FileUtils.cp list, "#{ migrations }/"
42
+ end
43
+
44
+ desc "g class_name", "Создать файл миграции в db/migrations"
45
+ def g(class_name)
46
+ tstamp = Time.now.strftime "%Y%m%d%H%M%S"
47
+ fname = ''
48
+ if Dir[ "#{ Cfg.root }/db/migrations/#{ tstamp }_*.rb" ].any?
49
+ counter = 0
50
+ while Dir[ "#{ Cfg.root }/db/migrations/#{ tstamp }#{ '%02d' % counter }_*.rb" ].any? do
51
+ counter += 1
52
+ end
53
+ fname = "#{ Cfg.root }/db/migrations/#{ tstamp }_#{ '%02d' % counter }_#{ class_name }.rb"
54
+ else
55
+ fname = "#{ Cfg.root }/db/migrations/#{ tstamp }_#{ class_name }.rb"
56
+ end
57
+ Log.info{ "Создаю файлик миграции #{ fname }" }
58
+ File.open( fname, 'w' ) do |f|
59
+ f.write <<~EFILE
60
+ Sequel.migration do
61
+ up do
62
+ create_table :#{ class_name } do
63
+ primary_key :id, type: :Bignum
64
+
65
+ column :created_at, DateTime, null: false, index: true, default: Sequel.lit("now()")
66
+ column :updated_at, DateTime, null: false, index: true, default: Sequel.lit("now()")
67
+ end
68
+ run <<~EUP
69
+ DO $$
70
+ BEGIN
71
+ --triggers
72
+ IF NOT EXISTS (SELECT 1 FROM pg_trigger WHERE tgname = '#{ class_name }_update_timestamp') THEN
73
+ CREATE TRIGGER #{ class_name }_update_timestamp
74
+ BEFORE INSERT OR UPDATE ON #{ class_name }
75
+ FOR EACH ROW EXECUTE PROCEDURE update_timestamp();
76
+ END IF;
77
+ END $$;
78
+ EUP
79
+ end
80
+ down { run 'DROP TABLE #{ class_name } CASCADE' }
81
+ end
82
+ EFILE
83
+ end
84
+ end
85
+
86
+ desc "m", "Мигрировать миграции, можно добавить имя файла"
87
+ def m(point = nil)
88
+ point = point_from_filename(point) || nil
89
+ Log.warn{ "Мигрирую базу #{ point }" }
90
+ if point
91
+ Sequel::Migrator.run(Db, "db/migrations", target: point )
92
+ else
93
+ Sequel::Migrator.run(Db, "db/migrations" )
94
+ end
95
+ v
96
+ end
97
+
98
+ desc "r", "Откатить базу в ноль, либо к указанному файлу"
99
+ def r(point = '0')
100
+ point = point_from_filename(point) || 0
101
+ Log.warn{ "Откатываю базу #{ point }" }
102
+ if point
103
+ Sequel::Migrator.run(Db, "db/migrations", :target => point )
104
+ else
105
+ Sequel::Migrator.run(Db, "db/migrations" )
106
+ end
107
+ v
108
+ end
109
+
110
+ desc "v" , "Напечатать текущую версию в базе"
111
+ def v
112
+ version =
113
+ if Db.tables.include?(:schema_migrations)
114
+ (f = Db[:schema_migrations].all).any? ? f.last[:filename] : 'пусто'
115
+ else
116
+ 'пусто'
117
+ end
118
+ puts "Последняя миграция: #{ version }"
119
+ end
120
+
121
+ desc "create", "Создать базу"
122
+ def create
123
+ rootdb = superdb
124
+ Log.warn{ "Создаю пользователя: #{ Cfg.db.user } и базу: #{ Cfg.db.database }." }
125
+ begin
126
+ rootdb["CREATE USER #{ Cfg.db.user } WITH LOGIN PASSWORD '#{ Cfg.db.password }'"].all
127
+ rescue Exception => e
128
+ Log.info{ e.message }
129
+ end
130
+ begin
131
+ rootdb["CREATE DATABASE #{ Cfg.db.database } OWNER #{ Cfg.db.user }"].all
132
+ rescue Exception => e
133
+ Log.info{ e.message }
134
+ end
135
+ end
136
+
137
+ desc "scratch", 'Удалить базу, загрузить все миграции заново'
138
+ def scratch(db = nil)
139
+ db = superdb
140
+ Log.debug{"Отключение от текущей базы"}
141
+ Db.disconnect
142
+ unless db.test_connection
143
+ Log.warn{"Не смог подключиться к базе для махинаций. #{ superuser.inspect }"}
144
+ exit 255
145
+ end
146
+ Log.warn{ "Удаляю базу #{ Cfg.db.database }" }
147
+ begin
148
+ db << "DROP DATABASE #{ Cfg.db.database }"
149
+ rescue Exception => e
150
+ Log.error e.message
151
+ end
152
+ create
153
+ m
154
+ end
155
+
156
+ no_commands do
157
+ # подключается к базе с правами админа
158
+ # жёстко закодировано, что админ базы 'postgres' без пароля, и схема 'public'
159
+ def superdb
160
+ if ! @rootdb || ! @rootdb.test_connection
161
+ superuser = Marshal.load(Marshal.dump( Cfg.db ))
162
+ superuser[:adapter] = 'postgres'
163
+ superuser[:user] = 'postgres'
164
+ superuser[:database] = 'postgres'
165
+ superuser.delete :password
166
+ Log.debug{"Попытка административного подключения #{ superuser.inspect }"}
167
+ @rootdb = Sequel.connect( superuser )
168
+ end
169
+ @rootdb
170
+ end
171
+
172
+ def point_from_filename(n)
173
+ Log.debug{"Поиск миграции по куску имени: '#{ n }'."}
174
+ return nil unless n
175
+ unless n =~ /^\d+/
176
+ Pathname.new( Dir["#{ Cfg.root }/db/migrations/*#{ n }*.rb"].sort.last ).basename.to_s[/(\d+)/, 1].to_i
177
+ else
178
+ n[/^(\d+)/, 1].to_i
179
+ end
180
+ end
181
+
182
+ end
183
+
184
+ end
185
+
186
+ Dbtask.start
@@ -0,0 +1,26 @@
1
+ Sequel.migration do
2
+ up do
3
+ run <<~ESTOREDPROC
4
+ CREATE OR REPLACE FUNCTION insert_timestamp() RETURNS trigger
5
+ LANGUAGE plpgsql
6
+ AS $$
7
+ BEGIN
8
+ IF NEW.created_at IS NULL THEN
9
+ NEW.created_at := now();
10
+ END IF;
11
+ RETURN NEW;
12
+ END $$;
13
+
14
+ CREATE OR REPLACE FUNCTION update_timestamp() RETURNS trigger
15
+ LANGUAGE plpgsql
16
+ AS $$
17
+ BEGIN
18
+ IF NEW.updated_at IS NULL THEN
19
+ NEW.updated_at := now();
20
+ END IF;
21
+ RETURN NEW;
22
+ END $$;
23
+ ESTOREDPROC
24
+ end
25
+ down { run 'DROP FUNCTION IF EXISTS insert_timestamp(); DROP FUNCTION IF EXISTS update_timestamp();' }
26
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cfgdatabase
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.4
4
+ version: 1.2.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - deemytch
8
8
  autorequire:
9
- bindir: bin
9
+ bindir: utils
10
10
  cert_chain: []
11
- date: 2021-02-10 00:00:00.000000000 Z
11
+ date: 2021-03-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: sequel
@@ -68,11 +68,14 @@ dependencies:
68
68
  version: '0'
69
69
  description:
70
70
  email: aspamkiller@yandex.ru
71
- executables: []
71
+ executables:
72
+ - migrate.rb
72
73
  extensions: []
73
74
  extra_rdoc_files: []
74
75
  files:
75
76
  - lib/app-database.rb
77
+ - utils/migrate.rb
78
+ - utils/migrations/00000000000000_created_at_trigger.rb
76
79
  homepage: https://github.com/deemytch/cfgdatabase
77
80
  licenses:
78
81
  - GPL-2.0
@@ -92,8 +95,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
92
95
  - !ruby/object:Gem::Version
93
96
  version: '0'
94
97
  requirements: []
95
- rubygems_version: 3.1.4
98
+ rubygems_version: 3.2.13
96
99
  signing_key:
97
100
  specification_version: 4
98
- summary: Удобная загрузка настроек Sequel.
101
+ summary: Удобная загрузка настроек Sequel и миграции для PostgreSQL.
99
102
  test_files: []