diff --git a/Gemfile.lock b/Gemfile.lock index 71fc08b..2e039a3 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,29 +1,33 @@ PATH remote: . specs: - omelettes (0.1.0) + omelettes (0.2.0) activerecord (~> 3.0) faker (~> 0.9.5) + progressbar GEM remote: http://rubygems.org/ specs: - activemodel (3.0.5) - activesupport (= 3.0.5) - builder (~> 2.1.2) - i18n (~> 0.4) - activerecord (3.0.5) - activemodel (= 3.0.5) - activesupport (= 3.0.5) - arel (~> 2.0.2) - tzinfo (~> 0.3.23) - activesupport (3.0.5) - arel (2.0.10) - builder (2.1.2) + activemodel (3.2.1) + activesupport (= 3.2.1) + builder (~> 3.0.0) + activerecord (3.2.1) + activemodel (= 3.2.1) + activesupport (= 3.2.1) + arel (~> 3.0.0) + tzinfo (~> 0.3.29) + activesupport (3.2.1) + i18n (~> 0.6) + multi_json (~> 1.0) + arel (3.0.0) + builder (3.0.0) diff-lcs (1.1.2) faker (0.9.5) i18n (~> 0.4) i18n (0.6.0) + multi_json (1.0.4) + progressbar (0.10.0) rspec (2.1.0) rspec-core (~> 2.1.0) rspec-expectations (~> 2.1.0) @@ -32,7 +36,7 @@ GEM rspec-expectations (2.1.0) diff-lcs (~> 1.1.2) rspec-mocks (2.1.0) - tzinfo (0.3.29) + tzinfo (0.3.31) PLATFORMS ruby diff --git a/lib/omelettes.rb b/lib/omelettes.rb index a9a33d0..ce047fd 100644 --- a/lib/omelettes.rb +++ b/lib/omelettes.rb @@ -4,6 +4,8 @@ require 'activerecord' unless defined?(ActiveRecord) end +require 'progressbar' + require 'omelettes/column' require 'omelettes/model_additions' require 'omelettes/obfuscate' diff --git a/lib/omelettes/column.rb b/lib/omelettes/column.rb index 5f8755d..b0ac82a 100644 --- a/lib/omelettes/column.rb +++ b/lib/omelettes/column.rb @@ -22,7 +22,7 @@ def self.default(name, value) case name when :hardened return value - when :name, :first_name, :last_name + when :first_name, :last_name return Faker::Name.send(name) when :city, :state, :country, :street_address, :street_name, :zip_code return Faker::Address.send(name) @@ -32,8 +32,10 @@ def self.default(name, value) return Faker::Internet.send(name) when :paragraph, :paragraphs, :sentence, :sentences, :words return Faker::Lorem.send(name) - when :phone + when :phone, :contact_phone, :fax return Faker::PhoneNumber.phone_number + when :url, :website + return Faker::Internet.domain_name else return Omelettes::Obfuscate.obfuscate(value) end diff --git a/lib/omelettes/model_additions.rb b/lib/omelettes/model_additions.rb index a21737f..c2fd919 100644 --- a/lib/omelettes/model_additions.rb +++ b/lib/omelettes/model_additions.rb @@ -23,15 +23,19 @@ def ignore(column_name) alias :harden :ignore end - def obfuscate(column_name) - column = self.class.column_config(column_name) - original_value = self.send(column_name) - if column - value = column.process(original_value) - else - value = Column.default(column_name, original_value) + def obfuscate(column_names) + attributes = {} + column_names.each do |column_name| + column = self.class.column_config(column_name) + original_value = self.send(column_name) + if column + value = column.process(original_value) + else + value = Column.default(column_name, original_value) + end + attributes[column_name] = value end - self.update_attribute(column_name, value) if original_value != value + self.class.where(:id => self.id).update_all(attributes) end def self.included(base) diff --git a/lib/omelettes/obfuscate.rb b/lib/omelettes/obfuscate.rb index 6b15d88..5830e20 100644 --- a/lib/omelettes/obfuscate.rb +++ b/lib/omelettes/obfuscate.rb @@ -5,20 +5,40 @@ def cook(silent=false) total_tables = 0 total_attributes = 0 Words.load(word_list || "/usr/share/dict/words") + processed = [] tables.each do |table| next if ignore_table?(table) - print "\nProcessing #{model(table).name}" unless silent + processed << table + pbar = ProgressBar.new(model(table).name, model(table).count) unless silent model(table).find_each do |object| - model(table).columns.each do |column| - next if ignore_column?(column.name) || column.type != :string - object.obfuscate(column.name) + begin + object.obfuscate(columns_for_table(table)) total_attributes += 1 + rescue => e + puts e.message + next + ensure + pbar.inc unless silent end - print "." unless silent end + pbar.finish unless silent total_tables += 1 end - print "\n" unless silent + if @callback + @callback.call + end + unless silent + puts " Obfuscation Report (the following tables and columns were processed)" + puts ("----------" * 8) + processed.each do |table| + label = model(table).name + columns_for_table(table).join(',').scan(/.{0,60}/).each do |columns| + puts "%20.20s | %-60.60s" % [label, columns] unless columns.blank? + label = "" + end + end + end + [total_tables, total_attributes] end @@ -36,6 +56,8 @@ def ignore_table?(table) ignore_tables.each do |ignore| return true if table.match(ignore).to_s == table end + return true unless columns_for_table(table).any? + return true if model(table).count == 0 false end @@ -46,13 +68,20 @@ def ignore_column?(column) false end + def columns_for_table(table) + @columns_for_table ||= {} + @columns_for_table[table] ||= model(table).columns.select {|column| !ignore_column?(column.name) && (column.type == :string || column.type == :text)}.map(&:name) + end + + def post_cook(&callback) + @callback = callback + end + def obfuscate(string) return nil if string.nil? - result = [] - string.split(/(\s+)|([[:punct:]])/).each do |word| - result << (word.match(/[a-zA-Z]+/).nil? ? word : Words.replace(word)) - end - result.join("") + string.split(/(\s+)|([[:punct:]])/).map do |word| + word.match(/[a-zA-Z]+/).nil? ? word : Words.replace(word) + end.join("") end attr_accessor :ignore_tables diff --git a/lib/omelettes/words.rb b/lib/omelettes/words.rb index 60f49d9..9293432 100644 --- a/lib/omelettes/words.rb +++ b/lib/omelettes/words.rb @@ -7,16 +7,16 @@ def word_hash end def add(word) - key = "#{word[0].downcase}#{word.length}" + key = "#{word[0,1].downcase}#{word.length}" @word_hash[key] ||= [] @word_hash[key] << word end def replace(word) - key = "#{word[0].downcase}#{word.length}" + key = "#{word[0,1].downcase}#{word.length}" valid_words = (@word_hash[key] || []) new_word = valid_words[rand(valid_words.size)] - return new_word.send(word[0].upcase == word[0] ? :capitalize : :downcase) unless new_word.nil? + return new_word.send(word[0,1].upcase == word[0,1] ? :capitalize : :downcase) unless new_word.nil? word end diff --git a/lib/tasks/omelettes.rake b/lib/tasks/omelettes.rake index 339fd79..be2293d 100644 --- a/lib/tasks/omelettes.rake +++ b/lib/tasks/omelettes.rake @@ -1,10 +1,6 @@ namespace :db do desc "Obfuscate the database with Omelettes" task :cook => :environment do - print "Are you sure you want to scramble all strings in the database? (y/n): " - input = $stdin.gets.strip - if input == "y" - Omelettes::Obfuscate.cook - end + Omelettes::Obfuscate.cook end end \ No newline at end of file diff --git a/omelettes.gemspec b/omelettes.gemspec index c9a2137..d3fc46e 100644 --- a/omelettes.gemspec +++ b/omelettes.gemspec @@ -1,6 +1,6 @@ Gem::Specification.new do |s| s.name = "omelettes" - s.version = "0.2.0" + s.version = "0.5.0" s.author = "Mark Sim" s.email = "mark@quarternotecoda.com" s.homepage = "http://github.com/marksim/omelettes" @@ -13,6 +13,7 @@ Gem::Specification.new do |s| s.add_development_dependency 'rspec', '~> 2.1.0' s.add_dependency 'faker', "~> 0.9.5" s.add_dependency 'activerecord', '~> 3.0' + s.add_dependency 'progressbar' s.rubyforge_project = s.name s.required_rubygems_version = ">= 1.3.4"