Class: SyncableRecord
- Inherits:
-
ActiveRecord::Base
- Object
- ActiveRecord::Base
- SyncableRecord
- Defined in:
- lib/syncer/syncable_record.rb
Class Method Summary collapse
-
.fix_associated_ids(remote_record) ⇒ Object
finds models associated with temp ids and assigns their real id to the record.
-
.sync(remote_state) ⇒ Object
syncs a remote data source with server data state remote state should be provided as a Hash returns a Hash response with state changes for the remote data site.
Instance Method Summary collapse
- #increment_version ⇒ Object
-
#matches?(remote_record) ⇒ Boolean
helper to match record with remote record info if remote record has version use it otherwise only id matches (for delete records).
-
#updated?(remote_record) ⇒ Boolean
helper that checks if remote record has been updated.
- #zero_version ⇒ Object
Class Method Details
.fix_associated_ids(remote_record) ⇒ Object
finds models associated with temp ids and assigns their real id to the record
44 45 46 47 48 49 50 51 52 53 54 55 56 57 |
# File 'lib/syncer/syncable_record.rb', line 44 def self.fix_associated_ids(remote_record) remote_record.keys.each do |key| # if the key is of the form xxx_tmp_id next unless /(?<foreign_key_prefix>.*)_tmp_id$/ =~ key # then find the xxx class next unless model_class = foreign_key_prefix.capitalize.to_class # find the class model that matches the tmp_id next unless model = model_class.find_by_tmp_id(remote_record["#{foreign_key_prefix}_tmp_id"]) # save the actual model id remote_record["#{foreign_key_prefix}_id"] = model.id # remove the xxx_tmp_id key & value remote_record.delete(key) end end |
.sync(remote_state) ⇒ Object
syncs a remote data source with server data state remote state should be provided as a Hash returns a Hash response with state changes for the remote data site
62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 |
# File 'lib/syncer/syncable_record.rb', line 62 def self.sync(remote_state) # TODO we do not currently check for id's per user # TODO check both when creating and when accessing # TODO we should at some point delete the tmp_ids remote_saved = remote_state['saved'] || [] remote_created = remote_state['created'] || [] remote_deleted = remote_state['deleted'] || [] remote_updated = remote_state['updated'] || [] # build list of all the server records known to remote remote_existing_records = remote_saved + remote_updated + remote_deleted # group server records by new, updated and deleted as relates to remote state all_records = self.all new_records = all_records.find_all do |record| !remote_existing_records.detect { |remote_record| record.matches?(remote_record) } end update_records = all_records.find_all do |record| remote_existing_records.detect { |remote_record| record.updated?(remote_record) } end delete_records = remote_existing_records.find_all do |remote_record| !all_records.detect { |record| record.matches?(remote_record) } end # created created = new_records # handle special case where we have an updated record that was deleted remotely # in that case we must recreate it special_records = [] update_records.each do |record| match = remote_deleted.detect { |remote_record| record.updated?(remote_record) } if match created << record special_records << record end end # cleanup by removing these special case records # no need to remove from remote_deleted as that we will confirm special_records.each { |record| update_records.delete(record) } # create all new remote records and add to created list if remote_created remote_created.each do |remote_record| fix_associated_ids(remote_record) rec = self.create(remote_record) if rec.persisted? response = rec.to_rjson created << response end end end # updated updated = update_records # update all updated remote records and add to updated list if remote_updated remote_updated.each do |remote_record| fix_associated_ids(remote_record) rec = self.find_by_id(remote_record['id']) # update the record only if found and if it has not been updated on the server already if rec && !rec.updated?(remote_record) attrs = remote_record.dup attrs.delete("id") # can't update id attrs.delete("version") # can't update version updated << rec if rec.update_attributes(attrs) end end end # deleted deleted = delete_records.map{ |r| {:id => r['id']} if r['id'] } deleted.delete(nil) # destroy all deleted remote records and add to deleted list if remote_deleted remote_deleted.each do |remote_record| rec = self.find_by_id(remote_record['id']) # destroy the record only if found and if it has not been updated on the server already if rec && !rec.updated?(remote_record) rec.destroy deleted << { :id => remote_record['id'] } if remote_record['id'] end end end response = Hash.new response[:create] = created unless created.empty? response[:update] = updated unless updated.empty? response[:delete] = deleted unless deleted.empty? response rescue => error { :error => "#{error}" } end |
Instance Method Details
#increment_version ⇒ Object
24 25 26 |
# File 'lib/syncer/syncable_record.rb', line 24 def increment_version increment!(:version) end |
#matches?(remote_record) ⇒ Boolean
helper to match record with remote record info if remote record has version use it otherwise only id matches (for delete records)
30 31 32 33 34 35 36 |
# File 'lib/syncer/syncable_record.rb', line 30 def matches?(remote_record) if remote_record['version'] id == remote_record['id'] && version >= remote_record['version'] else id == remote_record['id'] end end |
#updated?(remote_record) ⇒ Boolean
helper that checks if remote record has been updated
39 40 41 |
# File 'lib/syncer/syncable_record.rb', line 39 def updated?(remote_record) id == remote_record['id'] && version > remote_record['version'] end |
#zero_version ⇒ Object
20 21 22 |
# File 'lib/syncer/syncable_record.rb', line 20 def zero_version self.version = 0 end |