Class: Bible270::MigrationReconciler

Inherits:
Object
  • Object
show all
Defined in:
lib/bible270/migration_reconciler.rb

Overview

Repairs schema_migrations after a database is copied without matching migration bookkeeping. A migration is safe to mark only when every table, column and current index it contributes is already present.

Defined Under Namespace

Classes: Status

Constant Summary collapse

CHECKS =
{
  'CreateBible270Readers' => :check_readers,
  'CreateBible270Checkoffs' => :check_checkoffs,
  'CreateBible270Comments' => :check_comments,
  'CreateBible270SignInTokens' => :check_sign_in_tokens,
  'AddNamesToBible270Readers' => :check_names,
  'AddApprovalToBible270Comments' => :check_approval,
  'CreateBible270Settings' => :check_settings,
  'AddBibleVersionToBible270Readers' => :check_bible_version,
  'AddPartToBible270Checkoffs' => :check_checkoff_parts,
  'AddRememberTokenToBible270Readers' => :check_remember_token,
  'AddMentionNoticesToBible270Readers' => :check_mention_notices,
  'AddParentToBible270Comments' => :check_comment_parent,
  'CreateBible270Likes' => :check_likes,
  'AddBlueLetterBibleToBible270Readers' => :check_blue_letter_bible,
  'AddPassageSourceToBible270Readers' => :check_passage_source,
  'CreateActiveStorageTables' => :check_active_storage
}.freeze

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(connection:, migration_context:) ⇒ MigrationReconciler

Returns a new instance of MigrationReconciler.



36
37
38
39
# File 'lib/bible270/migration_reconciler.rb', line 36

def initialize(connection:, migration_context:)
  @connection = connection
  @migration_context = migration_context
end

Class Method Details

.for_current_databaseObject



31
32
33
34
# File 'lib/bible270/migration_reconciler.rb', line 31

def self.for_current_database
  context = ActiveRecord::MigrationContext.new(ActiveRecord::Migrator.migrations_paths)
  new(connection: ActiveRecord::Base.connection, migration_context: context)
end

Instance Method Details

#check_active_storageObject

The Bible270 installer can install Active Storage for reader avatars. A copied database can therefore have the same timestamp mismatch for Rails' generated Active Storage migration as it has for the engine migrations.



183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
# File 'lib/bible270/migration_reconciler.rb', line 183

def check_active_storage
  table_missing(
    :active_storage_blobs,
    columns: %i[id key filename content_type metadata service_name byte_size checksum created_at],
    indexes: [{ columns: %i[key], unique: true }]
  ) + table_missing(
    :active_storage_attachments,
    columns: %i[id name record_type record_id blob_id created_at],
    indexes: [
      { columns: %i[blob_id] },
      { columns: %i[record_type record_id name blob_id], unique: true }
    ],
    foreign_keys: [{ column: :blob_id, to_table: :active_storage_blobs }]
  ) + table_missing(
    :active_storage_variant_records,
    columns: %i[id blob_id variation_digest],
    indexes: [{ columns: %i[blob_id variation_digest], unique: true }],
    foreign_keys: [{ column: :blob_id, to_table: :active_storage_blobs }]
  )
end

#check_approvalObject



106
107
108
109
110
111
112
# File 'lib/bible270/migration_reconciler.rb', line 106

def check_approval
  table_missing(
    :bible270_comments,
    columns: %i[approved moderated_at],
    indexes: [{ columns: %i[approved day] }]
  )
end

#check_bible_versionObject



122
123
124
# File 'lib/bible270/migration_reconciler.rb', line 122

def check_bible_version
  table_missing(:bible270_readers, columns: %i[bible_version])
end

#check_blue_letter_bibleObject



170
171
172
173
174
# File 'lib/bible270/migration_reconciler.rb', line 170

def check_blue_letter_bible
  return [] if connection.column_exists?(:bible270_readers, :passage_source)

  table_missing(:bible270_readers, columns: %i[blue_letter_bible])
end

#check_checkoff_partsObject



126
127
128
129
130
131
132
# File 'lib/bible270/migration_reconciler.rb', line 126

def check_checkoff_parts
  table_missing(
    :bible270_checkoffs,
    columns: %i[part],
    indexes: [{ columns: %i[reader_id day track part], unique: true }]
  )
end

#check_checkoffsObject



69
70
71
72
73
74
75
76
# File 'lib/bible270/migration_reconciler.rb', line 69

def check_checkoffs
  table_missing(
    :bible270_checkoffs,
    columns: %i[id reader_id day track created_at updated_at],
    indexes: [{ columns: %i[reader_id] }, { columns: %i[day] }],
    foreign_keys: [{ column: :reader_id, to_table: :bible270_readers }]
  )
end

#check_comment_parentObject



146
147
148
149
150
151
152
153
# File 'lib/bible270/migration_reconciler.rb', line 146

def check_comment_parent
  table_missing(
    :bible270_comments,
    columns: %i[parent_id],
    indexes: [{ columns: %i[parent_id] }],
    foreign_keys: [{ column: :parent_id, to_table: :bible270_comments }]
  )
end

#check_commentsObject



78
79
80
81
82
83
84
85
# File 'lib/bible270/migration_reconciler.rb', line 78

def check_comments
  table_missing(
    :bible270_comments,
    columns: %i[id reader_id day track body created_at updated_at],
    indexes: [{ columns: %i[reader_id] }, { columns: %i[day created_at] }],
    foreign_keys: [{ column: :reader_id, to_table: :bible270_readers }]
  )
end

#check_likesObject



155
156
157
158
159
160
161
162
163
164
165
166
167
168
# File 'lib/bible270/migration_reconciler.rb', line 155

def check_likes
  table_missing(
    :bible270_likes,
    columns: %i[id reader_id comment_id created_at updated_at],
    indexes: [
      { columns: %i[reader_id comment_id], unique: true },
      { columns: %i[comment_id] }
    ],
    foreign_keys: [
      { column: :reader_id, to_table: :bible270_readers },
      { column: :comment_id, to_table: :bible270_comments }
    ]
  )
end

#check_mention_noticesObject



142
143
144
# File 'lib/bible270/migration_reconciler.rb', line 142

def check_mention_notices
  table_missing(:bible270_readers, columns: %i[notify_on_mention])
end

#check_namesObject



98
99
100
101
102
103
104
# File 'lib/bible270/migration_reconciler.rb', line 98

def check_names
  table_missing(
    :bible270_readers,
    columns: %i[first_name last_name],
    indexes: [{ columns: %i[last_name first_name] }]
  ) + table_missing(:bible270_sign_in_tokens, columns: %i[first_name last_name])
end

#check_passage_sourceObject



176
177
178
# File 'lib/bible270/migration_reconciler.rb', line 176

def check_passage_source
  table_missing(:bible270_readers, columns: %i[passage_source])
end

#check_readersObject



58
59
60
61
62
63
64
65
66
67
# File 'lib/bible270/migration_reconciler.rb', line 58

def check_readers
  table_missing(
    :bible270_readers,
    columns: %i[id display_name email avatar_url provider uid owner_type owner_id started_on created_at updated_at],
    indexes: [
      { columns: %i[owner_type owner_id] },
      { columns: %i[provider uid], unique: true }
    ]
  )
end

#check_remember_tokenObject



134
135
136
137
138
139
140
# File 'lib/bible270/migration_reconciler.rb', line 134

def check_remember_token
  table_missing(
    :bible270_readers,
    columns: %i[remember_token],
    indexes: [{ columns: %i[remember_token] }]
  )
end

#check_settingsObject



114
115
116
117
118
119
120
# File 'lib/bible270/migration_reconciler.rb', line 114

def check_settings
  table_missing(
    :bible270_settings,
    columns: %i[id key value created_at updated_at],
    indexes: [{ columns: %i[key], unique: true }]
  )
end

#check_sign_in_tokensObject



87
88
89
90
91
92
93
94
95
96
# File 'lib/bible270/migration_reconciler.rb', line 87

def 
  table_missing(
    :bible270_sign_in_tokens,
    columns: %i[id email token_digest display_name expires_at consumed_at created_at updated_at],
    indexes: [
      { columns: %i[token_digest], unique: true },
      { columns: %i[email created_at] }
    ]
  )
end

#mark_applied!(status) ⇒ Object

Raises:

  • (ArgumentError)


52
53
54
55
56
# File 'lib/bible270/migration_reconciler.rb', line 52

def mark_applied!(status)
  raise ArgumentError, "#{status.migration.name} is not reflected in the schema" unless status.complete?

  migration_context.schema_migration.create_version(status.migration.version.to_s)
end

#statusesObject



41
42
43
44
45
46
47
48
49
50
# File 'lib/bible270/migration_reconciler.rb', line 41

def statuses
  applied = migration_context.get_all_versions.map(&:to_i)
  migration_context.migrations.filter_map do |migration|
    check = CHECKS[migration.name]
    next unless check
    next if applied.include?(migration.version.to_i)

    Status.new(migration: migration, missing: public_send(check))
  end
end