/lib/merge_db/merger.rb

https://github.com/alovak/merge_db · Ruby · 190 lines · 155 code · 35 blank · 0 comment · 6 complexity · d4c41aa23d7c038e24aa4350fe1bc19b MD5 · raw file

  1. module MergeDb
  2. class Source < ActiveRecord::Base
  3. end
  4. class Target < ActiveRecord::Base
  5. end
  6. class Merger
  7. def initialize(params)
  8. @source_name = params[:source]
  9. @target_name = params[:target]
  10. end
  11. def prepare
  12. prepare_tables_in_target
  13. end
  14. def merge
  15. add_db_to_scope
  16. copy_data_from_source_to_target
  17. restore_association_references
  18. clean_backedup_primary_keys
  19. end
  20. def restore_associations
  21. restore_association_references
  22. clean_backedup_primary_keys
  23. end
  24. private
  25. def prepare_tables_in_target
  26. puts "Prepare target"
  27. pbar = ProgressBar.new("tables", target.tables.size)
  28. target.tables.each do |table|
  29. pbar.inc
  30. add_scope_id_column(table) if Configuration.scoped_tables.include? table
  31. target.columns(table)
  32. .find_all {|column| column.name =~ /id$/}
  33. .each do |column|
  34. add_backup_id_column(table, column)
  35. end
  36. end
  37. pbar.finish
  38. end
  39. def add_db_to_scope
  40. @scope_id = target.insert("insert into #{Configuration.scope_name.pluralize} (db_name) values ('#{@source_name}')")
  41. end
  42. def copy_data_from_source_to_target
  43. puts "Copy data into target"
  44. source.tables.each do |table|
  45. puts "\nCopy #{table} records "
  46. query = "select * from #{table}"
  47. records = source.select_all(query)
  48. pbar = ProgressBar.new("#{table}", records.size)
  49. records.each do |fixture|
  50. pbar.inc
  51. fixture_with_saved_ids = prepare_for_merge(fixture)
  52. if Configuration.scoped_tables.include? table
  53. fixture_with_saved_ids[scope_id_column] = @scope_id
  54. end
  55. target.insert_fixture(fixture_with_saved_ids, table)
  56. end
  57. pbar.finish
  58. end
  59. end
  60. def restore_association_references
  61. puts "Restore association references"
  62. target.tables.each do |table|
  63. query = "select * from #{table}"
  64. restored_columns = Hash.new {|hash, key| hash[key] = [] }
  65. records = target.select_all(query)
  66. pbar = ProgressBar.new("#{table}", records.size)
  67. records.each do |record|
  68. pbar.inc
  69. record.each do |column, old_id|
  70. if column =~ /__id$/ && !old_id.nil? && !restored_columns[column].include?(old_id)
  71. restored_columns[column] << old_id
  72. if association = find_association_by_old_id(column, old_id)
  73. new_id = association["id"]
  74. update_query = "update #{table} set #{normalize_column_name(column)} = #{new_id} where #{column} = #{old_id}"
  75. target.update(update_query)
  76. else
  77. ActiveRecord::Base.logger.error "Can't find #{column} with old id: #{old_id}"
  78. end
  79. end
  80. end
  81. end
  82. pbar.finish
  83. unless restored_columns.empty?
  84. columns_with_null = restored_columns.keys.collect {|column| "#{column} = NULL"}.join(", ")
  85. query = "update #{table} set #{columns_with_null}"
  86. target.execute(query)
  87. end
  88. end
  89. end
  90. def clean_backedup_primary_keys
  91. source.tables.each do |table|
  92. query = "update #{table} set _id = NULL"
  93. target.execute(query)
  94. end
  95. end
  96. def add_backup_id_column(table, column)
  97. target.add_column(table, backup_column_name(column.name), column.type)
  98. end
  99. def add_scope_id_column(table)
  100. target.add_column(table, scope_id_column, :integer)
  101. end
  102. def scope_id_column
  103. Configuration.scope_name + "_id"
  104. end
  105. def find_association_by_old_id(column, value)
  106. association_table = column.split("__").first.pluralize
  107. query = "select * from #{association_table} where _id = #{value}"
  108. association = target.select_one(query)
  109. end
  110. def prepare_for_merge(fixture)
  111. {}.tap do |new_fixture|
  112. fixture.each do |key, value|
  113. new_fixture[backup_column_name(key)] = value
  114. end
  115. end
  116. end
  117. def backup_column_name(name)
  118. if name =~ /id$/
  119. name = name.gsub(/id$/, "_id")
  120. else
  121. name
  122. end
  123. end
  124. def normalize_column_name(name)
  125. name.gsub(/__id$/, '_id')
  126. end
  127. def target
  128. @target_connection ||= connect(Target, @target_name)
  129. end
  130. def source
  131. @source_connection ||= connect(Source, @source_name)
  132. end
  133. def connect(constant, db_name)
  134. if db_name
  135. constant.establish_connection(Configuration.database[db_name])
  136. constant.connection
  137. end
  138. end
  139. end
  140. end