ref: master
plugins/orders/lib/serialized_synced_data.rb
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 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 |
module SerializedSyncedData def self.prepare_data _hash return {} unless _hash hash = {}; _hash.each do |key, value| next if value.blank? hash[key.to_sym] = value end hash end module ClassMethods def sync_serialized_field field, &block class_attribute :serialized_synced_fields unless self.respond_to? :serialized_synced_fields self.serialized_synced_fields ||= [] self.serialized_synced_fields << field attribute = "#{field}_id" field_data = "#{field}_data".to_sym field_data_without_sync = "#{field_data}_without_sync".to_sym serialize field_data before_save "fill_#{field_data}" # Rails doesn't define getters/setter for attributes if not self.method_defined? field_data and field_data.to_s.in? self.column_names define_method field_data do self[field_data] || {} end else define_method "#{field_data}_with_sync_default" do self.send("#{field_data}_without_sync_default") || {} end alias_method_chain field_data, :sync_default end if not self.method_defined? "#{field_data}=" and field_data.to_s.in? self.column_names define_method "#{field_data}=" do |value| self[field_data] = SerializedSyncedData.prepare_data value end else define_method "#{field_data}_with_prepare=" do |value| self.send "#{field_data}_without_prepare=", SerializedSyncedData.prepare_data(value) end alias_method_chain "#{field_data}=", :prepare end # return data from foreign registry if any data was synced yet define_method "#{field_data}_with_sync" do current_data = self.send field_data_without_sync if current_data.present? then current_data else self.send "#{field}_synced_data" end end alias_method_chain field_data, :sync # get the data to sync as defined define_method "#{field}_synced_data" do source = self.send field if block_given? data = SerializedSyncedData.prepare_data instance_exec(&block) elsif source.is_a? ApplicationRecord data = SerializedSyncedData.prepare_data source.attributes elsif source.is_a? Array data = source.map{ |s| SerializedSyncedData.prepare_data s.attributes } end || {} end define_method "sync_#{field_data}" do value = self.send "#{field}_synced_data" current = self.send field_data # leave old fields unchanged new_value = current.deep_merge value self.send "#{field_data}=", new_value end # fill and update data define_method "fill_#{field_data}" do return unless self.send(field_data_without_sync).blank? or self.send("#{attribute}_changed?") self.send "sync_#{field_data}" end include InstanceMethods end end module InstanceMethods def sync_serialized_data self.class.serialized_synced_fields.each do |field| self.send "sync_#{field}_data" end end end end |