Module: Timet::DatabaseSyncer
- Included in:
- DatabaseSyncHelper
- Defined in:
- lib/timet/database_syncer.rb
Overview
Module responsible for synchronizing local and remote databases
Constant Summary collapse
- ITEM_FIELDS =
%w[start end tag notes pomodoro updated_at created_at deleted].freeze
Class Method Summary collapse
- .extract_timestamp(item) ⇒ Object
- .format_status_message(id, item, source) ⇒ Object
- .log_local_only(id) ⇒ Object
- .report_sync_error(error) ⇒ Object
- .upload_local_database(remote_storage, bucket, local_db_path) ⇒ Object
Instance Method Summary collapse
- #add_remote_item(local_db, id, remote_item) ⇒ Object
- #get_insert_values(item) ⇒ Object
- #get_item_values(item, include_id_at_start: false) ⇒ Object
- #get_update_values(item) ⇒ Object
- #handle_database_differences(*args) ⇒ Object
- #handle_sync_error(error, *args) ⇒ Object
- #insert_item_from_hash(db, item) ⇒ Object
- #items_to_hash(items) ⇒ Object
- #log_local_wins(id, local_item) ⇒ Object
- #merge_and_track_changes?(local_db, id, local_item, remote_item) ⇒ Boolean
- #merge_item(*args) ⇒ Object
- #open_remote_database(remote_path) ⇒ Object
- #process_bidirectional_sync(local_db, local_items_by_id, remote_items_by_id) ⇒ Object
- #process_existing_item(id, local_item, remote_item, local_db) ⇒ Object
- #remote_wins?(_remote_item, remote_time, local_time) ⇒ Boolean
- #resolve_remote_wins(local_db, id, remote_item) ⇒ Object
- #sync_databases(*args) ⇒ Object
- #sync_single_item_and_flag(local_db, id, local_item, remote_item) ⇒ Object
- #sync_with_remote_database(*args) ⇒ Object
- #update_item_from_hash(db, item) ⇒ Object
Class Method Details
.extract_timestamp(item) ⇒ Object
173 174 175 |
# File 'lib/timet/database_syncer.rb', line 173 def (item) item['updated_at'].to_i end |
.format_status_message(id, item, source) ⇒ Object
186 187 188 189 |
# File 'lib/timet/database_syncer.rb', line 186 def (id, item, source) deleted = item['deleted'].to_i == 1 ? ' and deleted' : '' "#{source} item #{id} is newer#{deleted} - #{source == 'Remote' ? 'updating local' : 'will be uploaded'}" end |
.log_local_only(id) ⇒ Object
119 120 121 |
# File 'lib/timet/database_syncer.rb', line 119 def log_local_only(id) puts "Local item #{id} will be uploaded" end |
.report_sync_error(error) ⇒ Object
30 31 32 33 |
# File 'lib/timet/database_syncer.rb', line 30 def report_sync_error(error) puts "Error opening remote database: #{error.}" puts 'Uploading local database to replace corrupted remote database' end |
.upload_local_database(remote_storage, bucket, local_db_path) ⇒ Object
36 37 38 |
# File 'lib/timet/database_syncer.rb', line 36 def upload_local_database(remote_storage, bucket, local_db_path) remote_storage.upload_file(bucket, local_db_path, 'timet.db') end |
Instance Method Details
#add_remote_item(local_db, id, remote_item) ⇒ Object
124 125 126 127 |
# File 'lib/timet/database_syncer.rb', line 124 def add_remote_item(local_db, id, remote_item) puts "Adding remote item #{id} to local" insert_item_from_hash(local_db, remote_item) end |
#get_insert_values(item) ⇒ Object
192 193 194 195 196 |
# File 'lib/timet/database_syncer.rb', line 192 def get_insert_values(item) @database_fields ||= ITEM_FIELDS values = @database_fields.map { |field| item[field] } [item['id'], *values] end |
#get_item_values(item, include_id_at_start: false) ⇒ Object
203 204 205 |
# File 'lib/timet/database_syncer.rb', line 203 def get_item_values(item, include_id_at_start: false) include_id_at_start ? get_insert_values(item) : get_update_values(item) end |
#get_update_values(item) ⇒ Object
198 199 200 201 |
# File 'lib/timet/database_syncer.rb', line 198 def get_update_values(item) @database_fields ||= ITEM_FIELDS @database_fields.map { |field| item[field] } end |
#handle_database_differences(*args) ⇒ Object
8 9 10 11 12 13 14 15 16 |
# File 'lib/timet/database_syncer.rb', line 8 def handle_database_differences(*args) local_db, remote_storage, bucket, local_db_path, remote_path = args puts 'Differences detected between local and remote databases' begin sync_with_remote_database(local_db, remote_path, remote_storage, bucket, local_db_path) rescue SQLite3::Exception => e handle_sync_error(e, remote_storage: remote_storage, bucket: bucket, local_db_path: local_db_path) end end |
#handle_sync_error(error, *args) ⇒ Object
18 19 20 21 22 23 24 25 26 27 28 |
# File 'lib/timet/database_syncer.rb', line 18 def handle_sync_error(error, *args) first_arg = args.first if first_arg.is_a?(Hash) = first_arg remote_storage, bucket, local_db_path = .values_at(:remote_storage, :bucket, :local_db_path) else remote_storage, bucket, local_db_path = args end report_sync_error(error) upload_local_database(remote_storage, bucket, local_db_path) end |
#insert_item_from_hash(db, item) ⇒ Object
154 155 156 157 158 159 160 161 |
# File 'lib/timet/database_syncer.rb', line 154 def insert_item_from_hash(db, item) fields = ['id', *ITEM_FIELDS].join(', ') placeholders = Array.new(ITEM_FIELDS.length + 1, '?').join(', ') db.execute_sql( "INSERT INTO items (#{fields}) VALUES (#{placeholders})", get_insert_values(item) ) end |
#items_to_hash(items) ⇒ Object
178 179 180 |
# File 'lib/timet/database_syncer.rb', line 178 def items_to_hash(items) items.to_h { |item| [item['id'], item] } end |
#log_local_wins(id, local_item) ⇒ Object
149 150 151 152 |
# File 'lib/timet/database_syncer.rb', line 149 def log_local_wins(id, local_item) puts (id, local_item, 'Local') :remote_update end |
#merge_and_track_changes?(local_db, id, local_item, remote_item) ⇒ Boolean
106 107 108 109 110 111 112 113 114 115 116 117 |
# File 'lib/timet/database_syncer.rb', line 106 def merge_and_track_changes?(local_db, id, local_item, remote_item) local_time = (local_item) remote_time = (remote_item) if remote_time > local_time puts "Remote item #{id} is newer - updating local" update_item_from_hash(local_db, remote_item) elsif local_time > remote_time puts "Local item #{id} is newer - will be uploaded" end true end |
#merge_item(*args) ⇒ Object
133 134 135 136 137 138 139 140 141 |
# File 'lib/timet/database_syncer.rb', line 133 def merge_item(*args) local_db, id, local_item, remote_item = args local_time = (local_item) remote_time = (remote_item) return resolve_remote_wins(local_db, id, remote_item) if remote_wins?(remote_item, remote_time, local_time) log_local_wins(id, local_item) end |
#open_remote_database(remote_path) ⇒ Object
49 50 51 52 53 54 |
# File 'lib/timet/database_syncer.rb', line 49 def open_remote_database(remote_path) db_remote = SQLite3::Database.new(remote_path) raise 'Failed to initialize remote database' unless db_remote db_remote end |
#process_bidirectional_sync(local_db, local_items_by_id, remote_items_by_id) ⇒ Object
76 77 78 79 80 81 82 83 84 85 86 87 88 89 |
# File 'lib/timet/database_syncer.rb', line 76 def process_bidirectional_sync(local_db, local_items_by_id, remote_items_by_id) all_ids = (remote_items_by_id.keys + local_items_by_id.keys).uniq local_has_changes = false all_ids.each do |id| remote_item = remote_items_by_id[id] local_item = local_items_by_id[id] changed = sync_single_item_and_flag(local_db, id, local_item, remote_item) local_has_changes = true if changed end local_has_changes end |
#process_existing_item(id, local_item, remote_item, local_db) ⇒ Object
129 130 131 |
# File 'lib/timet/database_syncer.rb', line 129 def process_existing_item(id, local_item, remote_item, local_db) merge_item(local_db, id, local_item, remote_item) end |
#remote_wins?(_remote_item, remote_time, local_time) ⇒ Boolean
182 183 184 |
# File 'lib/timet/database_syncer.rb', line 182 def remote_wins?(_remote_item, remote_time, local_time) remote_time > local_time end |
#resolve_remote_wins(local_db, id, remote_item) ⇒ Object
143 144 145 146 147 |
# File 'lib/timet/database_syncer.rb', line 143 def resolve_remote_wins(local_db, id, remote_item) puts (id, remote_item, 'Remote') update_item_from_hash(local_db, remote_item) :local_update end |
#sync_databases(*args) ⇒ Object
56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 |
# File 'lib/timet/database_syncer.rb', line 56 def sync_databases(*args) local_db, remote_db, remote_storage, bucket, local_db_path = args local_items = local_db.execute_sql('SELECT * FROM items ORDER BY updated_at DESC') remote_items = remote_db.execute('SELECT * FROM items ORDER BY updated_at DESC') local_by_id = items_to_hash(local_items) remote_by_id = items_to_hash(remote_items) local_changes = process_bidirectional_sync(local_db, local_by_id, remote_by_id) if local_changes remote_storage.upload_file(bucket, local_db_path, 'timet.db') puts 'Changes uploaded to remote' else puts 'No local changes to upload' end puts 'Database sync completed' end |
#sync_single_item_and_flag(local_db, id, local_item, remote_item) ⇒ Object
91 92 93 94 95 96 97 98 99 100 101 102 103 104 |
# File 'lib/timet/database_syncer.rb', line 91 def sync_single_item_and_flag(local_db, id, local_item, remote_item) if !remote_item && local_item puts "Local item #{id} will be uploaded" true elsif !local_item && remote_item puts "Adding remote item #{id} to local" insert_item_from_hash(local_db, remote_item) false elsif local_item && remote_item merge_and_track_changes?(local_db, id, local_item, remote_item) else false end end |
#sync_with_remote_database(*args) ⇒ Object
41 42 43 44 45 46 47 |
# File 'lib/timet/database_syncer.rb', line 41 def sync_with_remote_database(*args) local_db, remote_path, remote_storage, bucket, local_db_path = args db_remote = open_remote_database(remote_path) db_remote.results_as_hash = true local_db.instance_variable_get(:@db).results_as_hash = true sync_databases(local_db, db_remote, remote_storage, bucket, local_db_path) end |
#update_item_from_hash(db, item) ⇒ Object
163 164 165 166 167 168 169 170 171 |
# File 'lib/timet/database_syncer.rb', line 163 def update_item_from_hash(db, item) fields = "#{ITEM_FIELDS.join(' = ?, ')} = ?" values = get_update_values(item) values << item['id'] db.execute_sql( "UPDATE items SET #{fields} WHERE id = ?", values ) end |