nokodiff 0.3.1 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4804027f53e38322927f18d80b25a0c9cdfa659154e56090ba61e895dbde68f8
4
- data.tar.gz: 855959815a1284325a50326d962127ff44feee8d16adfd74fd0e7af300b99ab9
3
+ metadata.gz: 9195a8ebd9adf0496df31641acd77e7fa84616b809ecb45cf86cd82346c3dd11
4
+ data.tar.gz: 7bc5d7fe060b960d7ac4fecf58f1f1e1f15b12d421a42552f0fb134cd61cc489
5
5
  SHA512:
6
- metadata.gz: e9484c5b5682e6d8299d1c8d7ff9b38d6a9c53c85ffc15cbef6bdfd234d3bd81df05ba90423924511db20381e44fb70c078cbbef15fdfea631ad5d583647aede
7
- data.tar.gz: 3563899d45760342ac2a70ad7977b35a8e7bfc5aeb058aba2a69ad567865a6a8de628a4240f687439e2f48e5ef3ddba1d82e0916d72910d89e2b149897fbe659
6
+ metadata.gz: cd3a8e7d3386676defd112e12a8153d0c2f4af5256fe5c503e81bb6e47ca45395e97d34f8756dd0ad9b600c2719c8c94a9dd9341de14130b502e17634b37c5d9
7
+ data.tar.gz: 8af7196b25ff0eb368046046895b625f8df80394d8fa09c5076ba6d16f6721aa869a20823f026726dc2a50fd30354250104b9336c7ab2599d0176a43c674248e
data/README.md CHANGED
@@ -22,6 +22,34 @@ or add it to your Gemfile
22
22
  gem "nokodiff"
23
23
  ```
24
24
 
25
+ ## Local Development
26
+
27
+ The gem includes a dummy Rails application located in `spec/dummy`. It can be used to preview `Nokodiff` in a real browser environment without needing to integrate with an external host app.
28
+
29
+ To get the dummy app running you need to:
30
+
31
+ ### Install dependencies
32
+
33
+ ```bash
34
+ bundle install
35
+ ```
36
+
37
+ ### Navigate to the app and install dependencies
38
+
39
+ ```bash
40
+ cd spec/dummy
41
+ bundle install
42
+ ```
43
+
44
+ ### Start the server
45
+
46
+ ```bash
47
+ bin/rails server
48
+ ```
49
+
50
+ The dummy app will now be running on `localhost:3000`
51
+ If you want to edit the content you are testing it is located in `spec/dummy/app/controllers/application_controller.rb`, alongside the code calling `Nokodiff.diff(@before, @after)`
52
+
25
53
  ## Usage
26
54
 
27
55
  In the controller:
@@ -9,56 +9,53 @@ $light-grey: #f3f2f1;
9
9
  $black: #0b0c0c;
10
10
 
11
11
  .compare-editions {
12
- border: 1px solid $light-grey;
12
+ position: relative;
13
13
  border-left: 40px solid $light-grey;
14
14
  padding: 15px;
15
15
 
16
16
  .diff {
17
- margin: 0 -15px;
18
- padding: 0 15px;
19
- word-wrap: break-word;
17
+ position: static;
20
18
  margin-bottom: 10px;
21
- position: relative;
22
19
 
23
- del,
24
- ins {
25
- text-decoration: none;
20
+ del, ins, ul, li {
21
+ position: static !important;
26
22
  display: block;
27
- padding: 2px 4px;
23
+ text-decoration: none;
28
24
  border-radius: 3px;
29
25
  }
30
- }
31
26
 
32
- del {
33
- background-color: $removed-color;
34
- padding-bottom: 2px;
27
+ del {
28
+ background-color: $removed-color;
29
+ padding-bottom: 2px;
35
30
 
36
- span.diff-marker {
37
- font-weight: normal;
38
- background-color: $diff-marker-removed-color;
39
- border-bottom: 2px dashed $black;
31
+ span.diff-marker {
32
+ font-weight: normal;
33
+ background-color: $diff-marker-removed-color;
34
+ border-bottom: 2px dashed $black;
35
+ }
40
36
  }
41
- }
42
37
 
43
- ins {
44
- background-color: $added-color;
45
- padding-bottom: 2px;
38
+ ins {
39
+ background-color: $added-color;
40
+ padding-bottom: 2px;
46
41
 
47
- span.diff-marker {
48
- font-weight: normal;
49
- background-color: $diff-marker-added-color;
50
- border-bottom: 2px dashed $black;
42
+ span.diff-marker {
43
+ font-weight: normal;
44
+ background-color: $diff-marker-added-color;
45
+ border-bottom: 2px dashed $black;
46
+ }
51
47
  }
52
- }
53
48
 
54
- del::before,
55
- ins::before {
56
- position: absolute;
57
- margin-left: -59px;
58
- width: 40px;
59
- text-align: center;
60
- top: 0;
61
- bottom: 0;
49
+ del::before,
50
+ ins::before {
51
+ position: absolute;
52
+ left: -40px;
53
+ width: 40px;
54
+ height: 1.5em;
55
+ line-height: 1.5em;
56
+ text-align: center;
57
+ z-index: 10;
58
+ }
62
59
  }
63
60
 
64
61
  del::before {
@@ -11,7 +11,7 @@ module Nokodiff
11
11
  when :unchanged
12
12
  unchanged_block(diff[:before])
13
13
  when :changed
14
- deleted_block(char_diff_html(diff[:before], diff[:after]).first) + added_block(char_diff_html(diff[:before], diff[:after]).last)
14
+ changed_block(diff[:before], diff[:after])
15
15
  when :deleted
16
16
  deleted_block(diff[:before])
17
17
  when :added
@@ -26,17 +26,80 @@ module Nokodiff
26
26
  before_nodes = @before.children.to_a
27
27
  after_nodes = @after.children.to_a
28
28
 
29
- max = [before_nodes.length, after_nodes.length].max
30
-
31
- max.times.map do |i|
32
- before_node = before_nodes[i]
33
- after_node = after_nodes[i]
29
+ before_html_strings = before_nodes.map { |n| n.to_html.strip }
30
+ after_html_strings = after_nodes.map { |n| n.to_html.strip }
31
+
32
+ Diff::LCS.sdiff(before_html_strings, after_html_strings).map do |change|
33
+ case change.action
34
+ when "="
35
+ {
36
+ status: :unchanged,
37
+ before: before_nodes[change.old_position],
38
+ after: after_nodes[change.new_position],
39
+ }
40
+ when "!"
41
+ {
42
+ status: :changed,
43
+ before: before_nodes[change.old_position],
44
+ after: after_nodes[change.new_position],
45
+ }
46
+ when "-"
47
+ {
48
+ status: :deleted,
49
+ before: before_nodes[change.old_position],
50
+ after: nil,
51
+ }
52
+ when "+"
53
+ {
54
+ status: :added,
55
+ before: nil,
56
+ after: after_nodes[change.new_position],
57
+ }
58
+ end
59
+ end
60
+ end
34
61
 
35
- set_change_status(before_node, after_node)
62
+ def changed_block(before_node, after_node)
63
+ if structurally_similar?(before_node, after_node)
64
+ inner_diff = Differ.new(before_node, after_node).to_html
65
+ rebuild_element(after_node, inner_diff)
66
+ elsif before_node.text? && after_node.text?
67
+ before_diff, after_diff = diff_raw_text(before_node, after_node)
68
+ deleted_block(before_diff) + added_block(after_diff)
69
+ else
70
+ before_diff, after_diff = diff_sub_elements(before_node, after_node)
71
+ deleted_block(before_diff) + added_block(after_diff)
36
72
  end
37
73
  end
38
74
 
39
- def char_diff_html(before_html, after_html)
75
+ def structurally_similar?(before_node, after_node)
76
+ before_node.element? &&
77
+ after_node.element? &&
78
+ before_node.name == after_node.name &&
79
+ before_node.name != "p"
80
+ end
81
+
82
+ def rebuild_element(template_node, inner_html)
83
+ result = template_node.dup
84
+ result.inner_html = inner_html
85
+ result.to_html
86
+ end
87
+
88
+ def diff_raw_text(before_text, after_text)
89
+ diff = Diff::LCS.sdiff(before_text.text.chars, after_text.text.chars)
90
+ before_fragment, after_fragment = Nokodiff::ChangesInFragments.new(diff).call
91
+ [merge_fragment_spans(before_fragment), merge_fragment_spans(after_fragment)]
92
+ end
93
+
94
+ def merge_fragment_spans(fragment)
95
+ doc = fragment.document
96
+ wrapper = Nokogiri::XML::Node.new("span", doc)
97
+ wrapper.inner_html = fragment.to_html
98
+ merge_adjacent_highlighted_changes(wrapper)
99
+ wrapper.inner_html
100
+ end
101
+
102
+ def diff_sub_elements(before_html, after_html)
40
103
  before_dup = before_html.dup
41
104
  after_dup = after_html.dup
42
105
 
@@ -48,20 +111,6 @@ module Nokodiff
48
111
  [before_fragment.to_html, after_fragment.to_html]
49
112
  end
50
113
 
51
- def set_change_status(before_node, after_node)
52
- if before_node && after_node
53
- if before_node.to_html.strip == after_node.to_html.strip
54
- { status: :unchanged, before: before_node, after: after_node }
55
- else
56
- { status: :changed, before: before_node, after: after_node }
57
- end
58
- elsif before_node
59
- { status: :deleted, before: before_node, after: nil }
60
- elsif after_node
61
- { status: :added, before: nil, after: after_node }
62
- end
63
- end
64
-
65
114
  def merge_adjacent_highlighted_changes(node)
66
115
  return unless node.element?
67
116
 
@@ -84,8 +133,8 @@ module Nokodiff
84
133
  node.name == "span" && node["class"] == "diff-marker"
85
134
  end
86
135
 
87
- def unchanged_block(html)
88
- html.to_s
136
+ def unchanged_block(node)
137
+ node.to_html
89
138
  end
90
139
 
91
140
  def deleted_block(html)
@@ -6,9 +6,5 @@ module Nokodiff
6
6
  n["class"] = "diff-marker"
7
7
  end
8
8
  end
9
-
10
- def highlighted_change(text_node)
11
- text_node.replace(highlight_changes(text_node.to_html, text_node.parent))
12
- end
13
9
  end
14
10
  end
@@ -31,9 +31,6 @@ module Nokodiff
31
31
  end
32
32
 
33
33
  def diff_text_node_content(before_text_node, after_text_node)
34
- return highlighted_change(before_text_node) if text_removed?(before_text_node, after_text_node)
35
- return highlighted_change(after_text_node) if text_added?(before_text_node, after_text_node)
36
-
37
34
  before_chars = before_text_node.text.chars
38
35
  after_chars = after_text_node.text.chars
39
36
 
@@ -44,13 +41,5 @@ module Nokodiff
44
41
  before_text_node.replace(before_fragment)
45
42
  after_text_node.replace(after_fragment)
46
43
  end
47
-
48
- def text_removed?(before_node, after_node)
49
- before_node && after_node.nil?
50
- end
51
-
52
- def text_added?(before_node, after_node)
53
- before_node.nil? && after_node
54
- end
55
44
  end
56
45
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Nokodiff
4
- VERSION = "0.3.1"
4
+ VERSION = "0.4.0"
5
5
  end
data/lib/nokodiff.rb CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
  require "nokogiri"
4
4
  require "diff-lcs"
5
+ require "byebug"
5
6
 
6
7
  require_relative "nokodiff/formatting_helpers"
7
8
  require_relative "nokodiff/version"
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: nokodiff
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.1
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - GOV.UK Dev
@@ -113,6 +113,20 @@ dependencies:
113
113
  - - "<"
114
114
  - !ruby/object:Gem::Version
115
115
  version: 8.1.3
116
+ - !ruby/object:Gem::Dependency
117
+ name: byebug
118
+ requirement: !ruby/object:Gem::Requirement
119
+ requirements:
120
+ - - ">="
121
+ - !ruby/object:Gem::Version
122
+ version: '0'
123
+ type: :runtime
124
+ prerelease: false
125
+ version_requirements: !ruby/object:Gem::Requirement
126
+ requirements:
127
+ - - ">="
128
+ - !ruby/object:Gem::Version
129
+ version: '0'
116
130
  - !ruby/object:Gem::Dependency
117
131
  name: diff-lcs
118
132
  requirement: !ruby/object:Gem::Requirement
@@ -230,7 +244,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
230
244
  - !ruby/object:Gem::Version
231
245
  version: '0'
232
246
  requirements: []
233
- rubygems_version: 4.0.7
247
+ rubygems_version: 4.0.9
234
248
  specification_version: 4
235
249
  summary: A Ruby Gem to highlight additions, deletions and character level changes
236
250
  while preserving original HTML