lib/lockbox/migrator.rb in lockbox-0.3.2 vs lib/lockbox/migrator.rb in lockbox-0.3.3

- old
+ new

@@ -11,46 +11,50 @@ end def rotate(attributes:) fields = {} attributes.each do |a| - # use key instad of v[:attribute] to make it more intuitive when migrating: true + # use key instead of v[:attribute] to make it more intuitive when migrating: true field = model.lockbox_attributes[a] raise ArgumentError, "Bad attribute: #{a}" unless field fields[a] = field end - perform(fields: fields) + perform(fields: fields, rotate: true) end # TODO add attributes option def migrate(restart:) fields = model.lockbox_attributes.select { |k, v| v[:migrating] } + # need blind indexes for building relation blind_indexes = model.respond_to?(:blind_indexes) ? model.blind_indexes.select { |k, v| v[:migrating] } : {} perform(fields: fields, blind_indexes: blind_indexes, restart: restart) end private - def perform(fields:, blind_indexes: [], restart: true) + def perform(fields:, blind_indexes: [], restart: true, rotate: false) relation = @relation - # remove true condition in 0.4.0 - if true || (defined?(ActiveRecord::Base) && base_relation.is_a?(ActiveRecord::Base)) + # unscope if passed a model + unless ar_relation?(relation) || mongoid_relation?(relation) relation = relation.unscoped + else + # TODO remove in 0.4.0 + relation = relation.unscoped end # convert from possible class to ActiveRecord::Relation or Mongoid::Criteria relation = relation.all unless restart attributes = fields.map { |_, v| v[:encrypted_attribute] } attributes += blind_indexes.map { |_, v| v[:bidx_attribute] } - if defined?(ActiveRecord::Relation) && relation.is_a?(ActiveRecord::Relation) + if ar_relation?(relation) base_relation = relation.unscoped or_relation = relation.unscoped attributes.each_with_index do |attribute, i| or_relation = @@ -66,11 +70,11 @@ relation = relation.or(attributes.map { |a| {a => nil} }) end end each_batch(relation) do |records| - migrate_records(records, fields: fields, blind_indexes: blind_indexes, restart: restart) + migrate_records(records, fields: fields, blind_indexes: blind_indexes, restart: restart, rotate: rotate) end end def each_batch(relation) if relation.respond_to?(:find_in_batches) @@ -90,28 +94,60 @@ end yield records if records.any? end end - def migrate_records(records, fields:, blind_indexes:, restart:) + def migrate_records(records, fields:, blind_indexes:, restart:, rotate:) # do computation outside of transaction # especially expensive blind index computation - records.each do |record| - fields.each do |k, v| - record.send("#{v[:attribute]}=", record.send(k)) if restart || !record.send(v[:encrypted_attribute]) + if rotate + records.each do |record| + fields.each do |k, v| + # update encrypted attribute directly to skip blind index computation + record.send("lockbox_direct_#{k}=", record.send(k)) + end end - blind_indexes.each do |k, v| - record.send("compute_#{k}_bidx") if restart || !record.send(v[:bidx_attribute]) + else + records.each do |record| + if restart + fields.each do |k, v| + record.send("#{v[:encrypted_attribute]}=", nil) + end + + blind_indexes.each do |k, v| + record.send("#{v[:bidx_attribute]}=", nil) + end + end + + fields.each do |k, v| + record.send("#{v[:attribute]}=", record.send(k)) unless record.send(v[:encrypted_attribute]) + end + + # with Blind Index 2.0, bidx_attribute should be already set for each record + blind_indexes.each do |k, v| + record.send("compute_#{k}_bidx") unless record.send(v[:bidx_attribute]) + end end end + # don't need to save records that went from nil => nil records.select! { |r| r.changed? } - with_transaction do - records.each do |record| - record.save!(validate: false) + if records.any? + with_transaction do + records.each do |record| + record.save!(validate: false) + end end end + end + + def ar_relation?(relation) + defined?(ActiveRecord::Relation) && relation.is_a?(ActiveRecord::Relation) + end + + def mongoid_relation?(relation) + defined?(Mongoid::Criteria) && relation.is_a?(Mongoid::Criteria) end def with_transaction if @transaction @relation.transaction do