-
-
Save samsonjs/b6574eef2edcc3e31ea713d2858b9e1e to your computer and use it in GitHub Desktop.
This class will use your existing FactoryBot factories to rewrite the data already in the db, keeping the associations and structure but replacing the content.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class Anonymizer | |
include ActiveSupport::Benchmarkable | |
attr_reader :factory_names, :callbacks | |
def initialize(factory_names = nil, callbacks = {}) | |
raise ArgumentError.new("You must be in development to use the anonymizer") unless Rails.env.development? | |
require Rails.root.join("spec/factories") unless FactoryBot.factories.count > 0 | |
@factory_names = [*factory_names].compact.map(&:to_sym) | |
@callbacks = default_callbacks.merge(callbacks).with_indifferent_access | |
end | |
def factories | |
@factories ||= FactoryBot.factories.select { |f| | |
factory_names.empty? || factory_names.include?(f.name) | |
}.reject { |f| | |
forbidden_factory_names.include? f.name.to_s | |
}.select { |f| | |
f.send(:parent).is_a? FactoryBot::NullFactory | |
} | |
end | |
def logger | |
Rails.logger | |
end | |
def valid? | |
factories.map do |f| | |
invalid = logger.silence { f.build_class.all.reject { |obj| obj.valid? } } | |
next if invalid.empty? | |
puts [invalid.count, f.build_class.name, "record".pluralize(invalid.count), "invalid:"].join(" ") | |
puts invalid.map { |o| " #%d: %s" % [o.id, o.errors.full_messages.join("; ")] } | |
[f.build_class, invalid] | |
end.compact.to_h | |
end | |
def call | |
puts "Anonymizing!" | |
benchmark "\nAnonymized!" do | |
User.transaction do | |
factories.map do |f| | |
anonymize_all(f) | |
end | |
end | |
end | |
end | |
def forbidden_factory_names # TODO customize this for your env | |
%w[ | |
add_class_names_here | |
] | |
end | |
def default_callbacks # TODO customize this for your env | |
{ | |
before_someclass: ->(instance, attrs) { attrs.delete(:attr_you_want_to_keep) }, | |
} | |
end | |
private | |
def callback(f, name, *args, &block) | |
[ | |
callbacks["#{name}_#{f.try(:name)}"], | |
callbacks[name] | |
].compact.map { |c| c.call(*args, &block) } | |
end | |
def dry_run? | |
callback(nil, :dry_run).any? | |
end | |
def anonymize_all(f) | |
logger.info "Anonymizing with #{f.name}" | |
benchmark "\nAnonymized with #{f.name}" do | |
logger.silence do | |
f.build_class.all.each do |obj| | |
anonymize(f, obj) | |
end | |
end | |
end | |
end | |
def anonymize(f, obj) | |
attrs = FactoryBot.attributes_for(f.name) | |
callback(f, :before, obj, attrs) | |
if attrs.present? | |
if dry_run? | |
obj.attributes = attrs | |
p obj, obj.errors.full_messages.join("; ") unless obj.valid? | |
else | |
obj.update! attrs | |
end | |
print "." | |
else | |
print "S" | |
end | |
callback(f, :after, obj) | |
obj | |
rescue ActiveRecord::RecordInvalid => e | |
p obj, obj.errors.full_messages.join("; ") unless obj.valid? | |
raise e | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment