You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

diff_matrices.rb 5.2 kB

2 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155
  1. module Cucumber
  2. module MultilineArgument
  3. class DataTable
  4. class DiffMatrices # :nodoc:
  5. attr_accessor :cell_matrix, :other_table_cell_matrix, :options
  6. def initialize(cell_matrix, other_table_cell_matrix, options)
  7. @cell_matrix = cell_matrix
  8. @other_table_cell_matrix = other_table_cell_matrix
  9. @options = options
  10. end
  11. def call
  12. prepare_diff
  13. perform_diff
  14. fill_in_missing_values
  15. raise_error if should_raise?
  16. end
  17. private
  18. attr_reader :row_indices, :original_width, :original_header, :padded_width, :missing_row_pos, :insert_row_pos
  19. def prepare_diff
  20. @original_width = cell_matrix[0].length
  21. @original_header = other_table_cell_matrix[0]
  22. pad_and_match
  23. @padded_width = cell_matrix[0].length
  24. @row_indices = Array.new(other_table_cell_matrix.length) { |n| n }
  25. end
  26. # Pads two cell matrices to same column width and matches columns according to header value.
  27. def pad_and_match
  28. cols = cell_matrix.transpose
  29. unmatched_cols = other_table_cell_matrix.transpose
  30. header_values = cols.map(&:first)
  31. matched_cols = []
  32. header_values.each_with_index do |v, i|
  33. mapped_index = unmatched_cols.index { |unmapped_col| unmapped_col.first == v }
  34. if mapped_index
  35. matched_cols << unmatched_cols.delete_at(mapped_index)
  36. else
  37. mark_as_missing(cols[i])
  38. empty_col = ensure_2d(other_table_cell_matrix).collect { SurplusCell.new(nil, self, -1) }
  39. empty_col.first.value = v
  40. matched_cols << empty_col
  41. end
  42. end
  43. unmatched_cols.each do
  44. empty_col = cell_matrix.collect { SurplusCell.new(nil, self, -1) }
  45. cols << empty_col
  46. end
  47. self.cell_matrix = ensure_2d(cols.transpose)
  48. self.other_table_cell_matrix = ensure_2d((matched_cols + unmatched_cols).transpose)
  49. end
  50. def mark_as_missing(col)
  51. col.each do |cell|
  52. cell.status = :undefined
  53. end
  54. end
  55. def ensure_2d(array)
  56. array[0].is_a?(Array) ? array : [array]
  57. end
  58. def perform_diff
  59. inserted = 0
  60. missing = 0
  61. last_change = nil
  62. changes.each do |change|
  63. if change.action == '-'
  64. @missing_row_pos = change.position + inserted
  65. cell_matrix[missing_row_pos].each { |cell| cell.status = :undefined }
  66. row_indices.insert(missing_row_pos, nil)
  67. missing += 1
  68. else # '+'
  69. @insert_row_pos = change.position + missing
  70. inserted_row = change.element
  71. inserted_row.each { |cell| cell.status = :comment }
  72. cell_matrix.insert(insert_row_pos, inserted_row)
  73. row_indices[insert_row_pos] = nil
  74. inspect_rows(cell_matrix[missing_row_pos], inserted_row) if last_change == '-'
  75. inserted += 1
  76. end
  77. last_change = change.action
  78. end
  79. end
  80. def changes
  81. require 'diff/lcs'
  82. diffable_cell_matrix = cell_matrix.dup.extend(::Diff::LCS)
  83. diffable_cell_matrix.diff(other_table_cell_matrix).flatten(1)
  84. end
  85. def inspect_rows(missing_row, inserted_row)
  86. missing_row.each_with_index do |missing_cell, col|
  87. inserted_cell = inserted_row[col]
  88. if missing_cell.value != inserted_cell.value && missing_cell.value.to_s == inserted_cell.value.to_s
  89. missing_cell.inspect!
  90. inserted_cell.inspect!
  91. end
  92. end
  93. end
  94. def fill_in_missing_values
  95. other_table_cell_matrix.each_with_index do |other_row, i|
  96. row_index = row_indices.index(i)
  97. row = cell_matrix[row_index] if row_index
  98. next unless row
  99. (original_width..padded_width).each do |col_index|
  100. surplus_cell = other_row[col_index]
  101. row[col_index].value = surplus_cell.value if row[col_index]
  102. end
  103. end
  104. end
  105. def missing_col
  106. cell_matrix[0].find { |cell| cell.status == :undefined }
  107. end
  108. def surplus_col
  109. padded_width > original_width
  110. end
  111. def misplaced_col
  112. cell_matrix[0] != original_header
  113. end
  114. def raise_error
  115. table = DataTable.from([[]])
  116. table.instance_variable_set :@cell_matrix, cell_matrix
  117. raise Different, table if should_raise?
  118. end
  119. def should_raise?
  120. [
  121. missing_row_pos && options.fetch(:missing_row, true),
  122. insert_row_pos && options.fetch(:surplus_row, true),
  123. missing_col && options.fetch(:missing_col, true),
  124. surplus_col && options.fetch(:surplus_col, false),
  125. misplaced_col && options.fetch(:misplaced_col, false)
  126. ].any?
  127. end
  128. end
  129. private_constant :DiffMatrices
  130. end
  131. end
  132. end

No Description

Contributors (1)