pliny 0.31.0 → 0.32.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
  SHA256:
3
- metadata.gz: 03b6f9bac86953d6bef8858e6c4c1f6f94f06073fac8175b9572728ffed5a276
4
- data.tar.gz: c20333f978e5f8e908b13f6eaf9d37a3dbdb0f10204c1db9a8b5f83b02fbac11
3
+ metadata.gz: 8de7550e51b73562d5fbf52d648945f5f07925cc5df89b5526faba9fc5b27075
4
+ data.tar.gz: 46f1ed81d71d0b9b70332085bc6dc61038a98d8f66ba2645db6651f355f85fd4
5
5
  SHA512:
6
- metadata.gz: e851ccdd1fb0a3d548fa672d209829e9cd8ecf69023d1c7a2fb6ad0fb84ee1616d2d3b2a761d11ba9c13a989d2fb5176c31d3eb6acf9a02e20c6e7bc4b01386f
7
- data.tar.gz: aefb3c02dc44ba282595bb9b2e85dc52e2891cb16ac6395693ab63bd79c603b7c68e9b93dea4c494c1b574e8be308b26a34e40040d4e71e0a74cb9a9f828a207
6
+ metadata.gz: 90130887007a82d06b841e6e4ca27f0f242f24eb782f273037a50a849d8a8a19ce73b7d4fb42ee75bc983b8cdfac57f02057e6322d11ab00678ef5c51b2d8d13
7
+ data.tar.gz: a77f028909da4a10a07db20f96e3fa872c351114395adbbbe500a1106bc0b3882676bdd94fbb52ec91a23676ba92cce2b84a76277a385321f29c16c64ede2fc1
@@ -62,6 +62,134 @@ module Pliny
62
62
  Integer(version)
63
63
  end
64
64
 
65
+ class MigrationStatus
66
+ attr_reader :filename
67
+ attr_accessor :present_on_disk, :present_in_database
68
+
69
+ def initialize(filename:)
70
+ @filename = filename
71
+ @present_on_disk = false
72
+ @present_in_database = false
73
+ end
74
+
75
+ def status
76
+ if present_on_disk
77
+ if present_in_database
78
+ :up
79
+ else
80
+ :down
81
+ end
82
+ else
83
+ if present_in_database
84
+ :file_missing
85
+ else
86
+ raise "error" # FIXME: better message
87
+ end
88
+ end
89
+ end
90
+ end
91
+
92
+ class MigrationStatusPresenter
93
+ PADDING = 2
94
+ UP = "UP".freeze
95
+ DOWN = "DOWN".freeze
96
+ FILE_MISSING = "FILE MISSING".freeze
97
+
98
+ STATUS_MAP = {
99
+ up: UP,
100
+ down: DOWN,
101
+ file_missing: FILE_MISSING
102
+ }.freeze
103
+
104
+ STATUS_OPTIONS = [
105
+ UP,
106
+ DOWN,
107
+ FILE_MISSING
108
+ ].freeze
109
+
110
+ attr_reader :migration_statuses
111
+
112
+ def initialize(migration_statuses:)
113
+ @migration_statuses = migration_statuses
114
+ end
115
+
116
+ def to_s
117
+ rows.join("\n")
118
+ end
119
+
120
+ def rows
121
+ header + statuses + footer
122
+ end
123
+
124
+ def header
125
+ [
126
+ barrier_row,
127
+ header_row,
128
+ barrier_row
129
+ ]
130
+ end
131
+
132
+ def statuses
133
+ migration_statuses.map { |status|
134
+ status_row(status)
135
+ }
136
+ end
137
+
138
+ def footer
139
+ [
140
+ barrier_row
141
+ ]
142
+ end
143
+
144
+ def barrier_row
145
+ "+#{'-' * (longest_status + PADDING)}+#{'-' * (longest_migration_name + PADDING)}+"
146
+ end
147
+
148
+ def header_row
149
+ "|#{'STATUS'.center(longest_status + PADDING)}|#{'MIGRATION'.center(longest_migration_name + PADDING)}|"
150
+ end
151
+
152
+ def status_row(migration_status)
153
+ "|#{STATUS_MAP[migration_status.status].center(longest_status + PADDING)}|#{' ' * (PADDING / 2)}#{migration_status.filename.ljust(longest_migration_name)}#{' ' * (PADDING / 2)}|"
154
+ end
155
+
156
+ private
157
+
158
+ def longest_migration_name
159
+ @longest_migration_name ||= migration_statuses.map(&:filename).max_by(&:length).length
160
+ end
161
+
162
+ def longest_status
163
+ STATUS_OPTIONS.max_by(&:length).length
164
+ end
165
+ end
166
+
167
+ def status
168
+ migrations_in_database = get_migrations_from_database
169
+ migrations_on_disk = Dir["#{MIGRATION_DIR}/*.rb"].map { |f| File.basename(f) }
170
+ total_set_of_migrations = (migrations_in_database | migrations_on_disk).sort_by(&:to_i)
171
+
172
+ migration_statuses = total_set_of_migrations.map { |filename|
173
+ status = MigrationStatus.new(filename: filename)
174
+ if migrations_on_disk.include?(filename)
175
+ status.present_on_disk = true
176
+ end
177
+
178
+ if migrations_in_database.include?(filename)
179
+ status.present_in_database = true
180
+ end
181
+ status
182
+ }
183
+
184
+ MigrationStatusPresenter.new(migration_statuses: migration_statuses).to_s
185
+ end
186
+
187
+ def get_migrations_from_database
188
+ return [] unless db.table_exists?(:schema_migrations)
189
+
190
+ db[:schema_migrations].order(Sequel.asc(:filename)).select_map(:filename)
191
+ end
192
+
65
193
  def rollback
66
194
  current_version = version
67
195
  return if current_version.zero?
@@ -15,6 +15,16 @@ begin
15
15
  end
16
16
  end
17
17
 
18
+ namespace :migrate do
19
+ desc "Get the status of migrations"
20
+ task :status do
21
+ # TODO: figure out how to handle multiple databases. Also why do we support multiple databases this way?
22
+ Pliny::DbSupport.run(database_urls.first, $stdout) do |helper|
23
+ puts helper.status
24
+ end
25
+ end
26
+ end
27
+
18
28
  desc "Run database migrations"
19
29
  task :migrate do
20
30
  next if Dir["./db/migrate/*.rb"].empty?
data/lib/pliny/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Pliny
2
- VERSION = "0.31.0"
2
+ VERSION = "0.32.0"
3
3
  end
data/lib/template/Gemfile CHANGED
@@ -4,7 +4,7 @@ ruby "2.4.0"
4
4
  gem "multi_json"
5
5
  gem "oj"
6
6
  gem "pg"
7
- gem "pliny", "~> 0.31"
7
+ gem "pliny", "~> 0.32"
8
8
  gem "pry"
9
9
  gem "puma", "~> 3"
10
10
  gem "rack-ssl"
@@ -114,4 +114,243 @@ describe Pliny::DbSupport do
114
114
  end
115
115
  end
116
116
  end
117
+
118
+ describe "MigrationStatus" do
119
+ let(:filename) { "1630551344_latest_change.rb" }
120
+ let(:migration_status) { described_class::MigrationStatus.new(filename: filename) }
121
+
122
+ describe "#status" do
123
+ context "given a migration present on disk" do
124
+ before do
125
+ migration_status.present_on_disk = true
126
+ end
127
+
128
+ context "and present in the database" do
129
+ before do
130
+ migration_status.present_in_database = true
131
+ end
132
+
133
+ it "is :up" do
134
+ assert_equal :up, migration_status.status
135
+ end
136
+ end
137
+
138
+ context "and is absent from the database" do
139
+ before do
140
+ migration_status.present_in_database = false
141
+ end
142
+
143
+ it "is :down" do
144
+ assert_equal :down, migration_status.status
145
+ end
146
+ end
147
+ end
148
+
149
+ context "given a migration absent from disk" do
150
+ before do
151
+ migration_status.present_on_disk = false
152
+ end
153
+
154
+ context "and present in the database" do
155
+ before do
156
+ migration_status.present_in_database = true
157
+ end
158
+
159
+ it "is :file_missing" do
160
+ assert_equal :file_missing, migration_status.status
161
+ end
162
+ end
163
+
164
+ context "and is absent from the database" do
165
+ before do
166
+ migration_status.present_in_database = false
167
+ end
168
+
169
+ it "is an error case" do
170
+ assert_raises RuntimeError do
171
+ migration_status.status
172
+ end
173
+ end
174
+ end
175
+ end
176
+ end
177
+ end
178
+
179
+ describe 'MigrationStatusPresenter' do
180
+ let(:filename) { '1630551344_latest_change.rb' }
181
+ let(:up_migration) { described_class::MigrationStatus.new(filename: "00#{filename}") }
182
+ let(:down_migration) { described_class::MigrationStatus.new(filename: "0#{filename}") }
183
+ let(:file_missing_migration) { described_class::MigrationStatus.new(filename: "000#{filename}") }
184
+ let(:migration_statuses) { [up_migration, down_migration, file_missing_migration] }
185
+ let(:presenter) { described_class::MigrationStatusPresenter.new(migration_statuses: migration_statuses) }
186
+
187
+ before do
188
+ up_migration.present_in_database = true
189
+ up_migration.present_on_disk = true
190
+ down_migration.present_in_database = false
191
+ down_migration.present_on_disk = true
192
+ file_missing_migration.present_in_database = true
193
+ file_missing_migration.present_on_disk = false
194
+ end
195
+
196
+ describe '#barrier_row' do
197
+ it 'pads to the longest_migration name' do
198
+ expectation = '+--------------+--------------------------------+'
199
+ assert_equal expectation, presenter.barrier_row
200
+ end
201
+ end
202
+
203
+ describe '#header_row' do
204
+ it 'pads to the longest migration name' do
205
+ expectation = '| STATUS | MIGRATION |'
206
+ assert_equal expectation, presenter.header_row
207
+ end
208
+ end
209
+
210
+ describe '#header' do
211
+ let(:barrier) { '+--------------+--------------------------------+' }
212
+ let(:header) { '| STATUS | MIGRATION |' }
213
+
214
+ it 'wraps the title in barriers' do
215
+ assert_equal [barrier, header, barrier], presenter.header
216
+ end
217
+ end
218
+
219
+ describe '#footer' do
220
+ let(:barrier) { '+--------------+--------------------------------+' }
221
+
222
+ it 'just a barrier' do
223
+ assert_equal [barrier], presenter.footer
224
+ end
225
+ end
226
+
227
+ describe '#status_row' do
228
+ context 'an up migration' do
229
+ it 'shows the correct details' do
230
+ expectation = '| UP | 001630551344_latest_change.rb |'
231
+ assert_equal expectation, presenter.status_row(up_migration)
232
+ end
233
+ end
234
+
235
+ context 'a down migration' do
236
+ it 'shows the correct details' do
237
+ expectation = '| DOWN | 01630551344_latest_change.rb |'
238
+ assert_equal expectation, presenter.status_row(down_migration)
239
+ end
240
+ end
241
+
242
+ context 'a file missing migration' do
243
+ it 'shows the correct details' do
244
+ expectation = '| FILE MISSING | 0001630551344_latest_change.rb |'
245
+ assert_equal expectation, presenter.status_row(file_missing_migration)
246
+ end
247
+ end
248
+ end
249
+
250
+ describe '#statuses' do
251
+ let(:up_expectation) { '| UP | 001630551344_latest_change.rb |' }
252
+ let(:down_expectation) { '| DOWN | 01630551344_latest_change.rb |' }
253
+ let(:file_missing_expectation) { '| FILE MISSING | 0001630551344_latest_change.rb |' }
254
+
255
+ it 'returns strings' do
256
+ assert_equal [up_expectation, down_expectation, file_missing_expectation], presenter.statuses
257
+ end
258
+ end
259
+
260
+ describe '#rows' do
261
+ let(:barrier) { '+--------------+--------------------------------+' }
262
+ let(:header) { '| STATUS | MIGRATION |' }
263
+ let(:up_expectation) { '| UP | 001630551344_latest_change.rb |' }
264
+ let(:down_expectation) { '| DOWN | 01630551344_latest_change.rb |' }
265
+ let(:file_missing_expectation) { '| FILE MISSING | 0001630551344_latest_change.rb |' }
266
+ let(:footer) { '+--------------+--------------------------------+' }
267
+
268
+ it 'is the table as an array' do
269
+ expectation = [
270
+ barrier,
271
+ header,
272
+ barrier,
273
+ up_expectation,
274
+ down_expectation,
275
+ file_missing_expectation,
276
+ footer
277
+ ]
278
+
279
+ assert_equal expectation, presenter.rows
280
+ end
281
+ end
282
+
283
+ describe '#to_s' do
284
+ it 'is the table as a string' do
285
+ expectation = <<~OUTPUT.chomp
286
+ +--------------+--------------------------------+
287
+ | STATUS | MIGRATION |
288
+ +--------------+--------------------------------+
289
+ | UP | 001630551344_latest_change.rb |
290
+ | DOWN | 01630551344_latest_change.rb |
291
+ | FILE MISSING | 0001630551344_latest_change.rb |
292
+ +--------------+--------------------------------+
293
+ OUTPUT
294
+
295
+ assert_equal expectation, presenter.to_s
296
+ end
297
+ end
298
+ end
299
+
300
+ describe '#status' do
301
+ let(:filename) { '1630551344_latest_change.rb' }
302
+ let(:up_migration) { "00#{filename}" }
303
+ let(:down_migration) { "0#{filename}" }
304
+ let(:file_missing_migration) { "000#{filename}" }
305
+
306
+ before do
307
+ DB.create_table(:schema_migrations) do
308
+ text :filename
309
+ end
310
+
311
+ migrations = DB[:schema_migrations]
312
+ migrations.insert(filename: up_migration)
313
+ migrations.insert(filename: file_missing_migration)
314
+
315
+ File.open("./db/migrate/#{up_migration}", "w") do |f|
316
+ f.puts "
317
+ Sequel.migration do
318
+ change do
319
+ create_table(:foo) do
320
+ primary_key :id
321
+ text :bar
322
+ end
323
+ end
324
+ end
325
+ "
326
+ end
327
+
328
+ File.open("./db/migrate/#{down_migration}", "w") do |f|
329
+ f.puts "
330
+ Sequel.migration do
331
+ change do
332
+ create_table(:foo) do
333
+ primary_key :id
334
+ text :bar
335
+ end
336
+ end
337
+ end
338
+ "
339
+ end
340
+ end
341
+
342
+ it 'returns a table string' do
343
+ expectation = <<~OUTPUT.chomp
344
+ +--------------+--------------------------------+
345
+ | STATUS | MIGRATION |
346
+ +--------------+--------------------------------+
347
+ | FILE MISSING | 0001630551344_latest_change.rb |
348
+ | UP | 001630551344_latest_change.rb |
349
+ | DOWN | 01630551344_latest_change.rb |
350
+ +--------------+--------------------------------+
351
+ OUTPUT
352
+
353
+ assert_equal expectation, support.status
354
+ end
355
+ end
117
356
  end
@@ -1,30 +1,46 @@
1
1
  require "spec_helper"
2
+ require "open3"
3
+ require 'active_support/core_ext/object/blank'
2
4
 
3
5
  describe "Pliny integration test" do
4
- before(:all) do
5
- @original_dir = Dir.pwd
6
-
7
- test_dir = Dir.mktmpdir("plinytest-")
8
- Dir.chdir(test_dir)
9
-
10
- bash "pliny-new myapp"
6
+ describe "bin/setup" do
7
+ around do |example|
8
+ original_dir = Dir.pwd
9
+ test_dir = Dir.mktmpdir("plinytest-")
10
+ Dir.chdir(test_dir)
11
11
 
12
- Dir.chdir("myapp")
13
- bash "bin/setup"
14
- end
12
+ bash "pliny-new myapp"
15
13
 
16
- after(:all) do
17
- Dir.chdir(@original_dir)
18
- end
14
+ Dir.chdir("myapp")
15
+ example.run
16
+ Dir.chdir(original_dir)
17
+ end
19
18
 
20
- describe "bin/setup" do
21
19
  it "generates .env" do
20
+ bash "bin/setup"
21
+
22
22
  assert File.exist?("./.env")
23
23
  end
24
24
  end
25
25
 
26
26
  describe "pliny-generate scaffold" do
27
- before(:all) do
27
+ around do |example|
28
+ DB.tables.each { |t| DB.drop_table(t) }
29
+
30
+ original_dir = Dir.pwd
31
+ test_dir = Dir.mktmpdir("plinytest-")
32
+ Dir.chdir(test_dir)
33
+
34
+ bash "pliny-new myapp"
35
+
36
+ Dir.chdir("myapp")
37
+ bash "bin/setup"
38
+
39
+ example.run
40
+ Dir.chdir(original_dir)
41
+ end
42
+
43
+ before do
28
44
  bash "pliny-generate scaffold artist"
29
45
  end
30
46
 
@@ -45,6 +61,71 @@ describe "Pliny integration test" do
45
61
  end
46
62
  end
47
63
 
64
+ describe "rake db:migrate:status" do
65
+ around do |example|
66
+ DB.tables.each { |t| DB.drop_table(t) }
67
+
68
+ original_dir = Dir.pwd
69
+ test_dir = Dir.mktmpdir("plinytest-")
70
+ Dir.chdir(test_dir)
71
+
72
+ bash "pliny-new myapp"
73
+
74
+ Dir.chdir("myapp")
75
+ bash "bin/setup"
76
+
77
+ example.run
78
+ Dir.chdir(original_dir)
79
+ end
80
+
81
+ it "returns a migration in the DOWN state when not migrated" do
82
+ bash "pliny-generate model artist"
83
+ migration_file = Dir.glob('db/migrate/*').first
84
+
85
+ stdout, stderr = bash_with_output("rake db:migrate:status")
86
+
87
+ statuses = Hash[stdout.to_s.split(/\+[-]+\+[-]+\+/)[2..-1].map { |s| s.tr("\n", "") }.select(&:present?).map { |s| s.split("|").map { |s| s.tr(" ", "") }.select(&:present?).reverse }]
88
+ assert statuses[migration_file.split("/").last] == "DOWN"
89
+ end
90
+
91
+ it "returns a migration in the UP state when not migrated" do
92
+ bash "pliny-generate model artist"
93
+ migration_file = Dir.glob('db/migrate/*').first
94
+ bash "rake db:migrate"
95
+
96
+ stdout, stderr = bash_with_output("rake db:migrate:status")
97
+
98
+ statuses = Hash[stdout.to_s.split(/\+[-]+\+[-]+\+/)[2..-1].map { |s| s.tr("\n", "") }.select(&:present?).map { |s| s.split("|").map { |s| s.tr(" ", "") }.select(&:present?).reverse }]
99
+ assert statuses[migration_file.split("/").last] == "UP"
100
+ end
101
+
102
+ it "returns a migration in the FILE MISSING state when the file is missing" do
103
+ bash "pliny-generate model artist"
104
+ migration_file = Dir.glob('db/migrate/*').first
105
+ bash "rake db:migrate"
106
+
107
+ FileUtils.rm_f(migration_file)
108
+
109
+ stdout, stderr = bash_with_output("rake db:migrate:status")
110
+
111
+ statuses = Hash[stdout.to_s.split(/\+[-]+\+[-]+\+/)[2..-1].map { |s| s.tr("\n", "") }.select(&:present?).map { |s| s.split("|").map { |s| s.gsub(/(^[ ]+|[ ]+$)/, "") }.select(&:present?).reverse }]
112
+ assert statuses[migration_file.split("/").last] == "FILE MISSING"
113
+ end
114
+ end
115
+
116
+ def bash_with_output(cmd)
117
+ bin = File.expand_path('../bin', File.dirname(__FILE__))
118
+ path = "#{bin}:#{ENV["PATH"]}"
119
+ env = { "PATH" => path }
120
+ stdout, stderr, status = Open3.capture3(env, cmd)
121
+
122
+ unless status.success?
123
+ raise "Failed to run #{cmd}, error was #{stderr}"
124
+ end
125
+
126
+ return stdout, stderr
127
+ end
128
+
48
129
  def bash(cmd)
49
130
  bin = File.expand_path('../bin', File.dirname(__FILE__))
50
131
  path = "#{bin}:#{ENV["PATH"]}"
metadata CHANGED
@@ -1,15 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pliny
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.31.0
4
+ version: 0.32.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Brandur Leach
8
8
  - Pedro Belo
9
- autorequire:
9
+ autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2021-09-02 00:00:00.000000000 Z
12
+ date: 2022-02-03 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: activesupport
@@ -497,7 +497,7 @@ homepage: https://github.com/interagent/pliny
497
497
  licenses:
498
498
  - MIT
499
499
  metadata: {}
500
- post_install_message:
500
+ post_install_message:
501
501
  rdoc_options: []
502
502
  require_paths:
503
503
  - lib
@@ -512,8 +512,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
512
512
  - !ruby/object:Gem::Version
513
513
  version: '0'
514
514
  requirements: []
515
- rubygems_version: 3.1.6
516
- signing_key:
515
+ rubygems_version: 3.1.2
516
+ signing_key:
517
517
  specification_version: 4
518
518
  summary: Basic tooling to support API apps in Sinatra
519
519
  test_files: []