class Anony::Strategies::Overwrite

The interface for configuring a field-level strategy. All of the methods here are made available inside the `overwrite { … }` block:

@example

anonymise do
  overwrite do
    nilable :first_name
    email :email_address
    with_strategy(:last_name) { "last-#{id}" }
  end
end

Attributes

anonymisable_fields[R]

A hash containing the fields and their anonymisation strategies.

Public Class Methods

new(model_class, &block) click to toggle source

@!visibility private

# File lib/anony/strategies/overwrite.rb, line 22
def initialize(model_class, &block)
  @model_class = model_class
  @anonymisable_fields = {}
  instance_eval(&block) if block
end

Public Instance Methods

apply(instance) click to toggle source

Apply the Overwrite strategy on the model instance, which applies each of the configured transformations and updates the :anonymised_at field if it exists.

@param [ActiveRecord::Base] instance An instance of the model

# File lib/anony/strategies/overwrite.rb, line 47
def apply(instance)
  if !@anonymisable_fields.key?(:anonymised_at) &&
      @model_class.column_names.include?("anonymised_at")
    current_datetime(:anonymised_at)
  end

  @anonymisable_fields.each_key do |field|
    anonymise_field(instance, field)
  end

  result_fields = instance.changes.keys.map(&:to_sym).reject { |s| s == :anonymised_at }

  instance.save!

  Result.overwritten(result_fields)
end
hex(*fields, max_length: 36) click to toggle source

Helper method to use the :hex strategy @param [Array<Symbol>] fields A list of one or more fields to apply this strategy to. @see Strategies::OverwriteHex

@example

hex :first_name
# File lib/anony/strategies/overwrite.rb, line 112
def hex(*fields, max_length: 36)
  with_strategy(Strategies::OverwriteHex.new(max_length), *fields)
end
ignore(*fields) click to toggle source

Configure a list of fields that you don't want to anonymise.

@param [Array<Symbol>] fields The fields to ignore @raise [ArgumentError] If trying to ignore a field which is already globally

ignored in Anony::Config.ignores

@example

ignore :external_system_id, :externalised_at
# File lib/anony/strategies/overwrite.rb, line 124
def ignore(*fields)
  already_ignored = fields.select { |field| Config.ignore?(field) }

  if already_ignored.any?
    raise ArgumentError, "Cannot ignore #{already_ignored.inspect} " \
                        "(fields already ignored in Anony::Config)"
  end

  no_op(*fields)
end
valid?() click to toggle source

Check whether the combination of field-level rules is valid

# File lib/anony/strategies/overwrite.rb, line 32
def valid?
  validate!
  true
rescue FieldException
  false
end
validate!() click to toggle source
# File lib/anony/strategies/overwrite.rb, line 39
def validate!
  raise FieldException, unhandled_fields if unhandled_fields.any?
end
with_strategy(strategy, *fields, &block) click to toggle source

Configure a custom strategy for one or more fields. If a block is given that is used as the strategy, otherwise the first argument is used as the strategy.

@param [Proc, Object] strategy Any object which responds to

`.call(previous_value)`. Not used if a block is provided.

@param [Array<Symbol>] fields A list of one or more fields to apply this strategy to. @param [Block] &block A block to use as the strategy. @yieldparam previous [Object] The previous value of the field @yieldreturn [Object] The value to set on that field. @raise [ArgumentError] If the combination of strategy, fields and block is invalid. @raise [DuplicateStrategyException] If more than one strategy is defined for the same field.

@example With a named class

class Reverse
  def self.call(previous)
    previous.reverse
  end
end

with_strategy(Reverse, :first_name)

@example With a constant value

with_strategy({}, :metadata)

@example With a block

with_strategy(:first_name, :last_name) { |previous| previous.reverse }
# File lib/anony/strategies/overwrite.rb, line 90
def with_strategy(strategy, *fields, &block)
  if block
    fields.unshift(strategy)
    strategy = block
  end

  fields = fields.flatten

  raise ArgumentError, "Block or Strategy object required" unless strategy
  raise ArgumentError, "One or more fields required" unless fields.any?

  guard_duplicate_strategies!(fields)

  fields.each { |field| @anonymisable_fields[field] = strategy }
end

Private Instance Methods

anonymise_field(instance, field) click to toggle source
# File lib/anony/strategies/overwrite.rb, line 146
        def anonymise_field(instance, field)
  return unless @model_class.column_names.include?(field.to_s)

  strategy = @anonymisable_fields.fetch(field)
  current_value = instance.read_attribute(field)

  instance.write_attribute(field, anonymised_value(instance, strategy, current_value))
end
anonymised_value(instance, strategy, current_value) click to toggle source
# File lib/anony/strategies/overwrite.rb, line 155
        def anonymised_value(instance, strategy, current_value)
  if strategy.is_a?(Proc)
    instance.instance_exec(current_value, &strategy)
  elsif strategy.respond_to?(:call)
    strategy.call(current_value)
  else
    strategy
  end
end
guard_duplicate_strategies!(fields) click to toggle source
# File lib/anony/strategies/overwrite.rb, line 165
        def guard_duplicate_strategies!(fields)
  defined_fields = @anonymisable_fields.keys
  duplicate_fields = defined_fields & fields

  raise DuplicateStrategyException, duplicate_fields if duplicate_fields.any?
end
unhandled_fields() click to toggle source
# File lib/anony/strategies/overwrite.rb, line 135
        def unhandled_fields
  anonymisable_columns =
    @model_class.column_names.map(&:to_sym).
      reject { |c| Config.ignore?(c) }.
      reject { |c| c == :anonymised_at }

  handled_fields = @anonymisable_fields.keys

  anonymisable_columns - handled_fields
end