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,
  'AddDailyRemindersToBible270Readers' => :check_daily_reminders,
  'AddReflectionsSeenAtToBible270Readers' => :check_reflections_seen_at,
  '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.



38
39
40
41
# File 'lib/bible270/migration_reconciler.rb', line 38

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

Class Method Details

.for_current_databaseObject



33
34
35
36
# File 'lib/bible270/migration_reconciler.rb', line 33

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.



196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
# File 'lib/bible270/migration_reconciler.rb', line 196

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



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

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

#check_bible_versionObject



124
125
126
# File 'lib/bible270/migration_reconciler.rb', line 124

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

#check_blue_letter_bibleObject



172
173
174
175
176
# File 'lib/bible270/migration_reconciler.rb', line 172

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



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

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

#check_checkoffsObject



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

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



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

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



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

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_daily_remindersObject



182
183
184
185
186
187
# File 'lib/bible270/migration_reconciler.rb', line 182

def check_daily_reminders
  table_missing(
    :bible270_readers,
    columns: %i[daily_reminders daily_reminder_time last_daily_reminder_sent_on]
  )
end

#check_likesObject



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

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



144
145
146
# File 'lib/bible270/migration_reconciler.rb', line 144

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

#check_namesObject



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

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



178
179
180
# File 'lib/bible270/migration_reconciler.rb', line 178

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

#check_readersObject



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

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_reflections_seen_atObject



189
190
191
# File 'lib/bible270/migration_reconciler.rb', line 189

def check_reflections_seen_at
  table_missing(:bible270_readers, columns: %i[reflections_seen_at])
end

#check_remember_tokenObject



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

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

#check_settingsObject



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

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



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

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)


54
55
56
57
58
# File 'lib/bible270/migration_reconciler.rb', line 54

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



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

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