nokodiff 0.1.0 → 0.3.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: ec76ca57f4fd914bc778242d825a9a074dd39bff32b8467e4729592f31bfb0ab
4
- data.tar.gz: 67de4318dc9d455b212a8199c2039acfbf63b2b078c90e06e26e0ac00a3f17ef
3
+ metadata.gz: e12bce0257af1ebff2c6de595dce5d68cefc73feb5ae4a419a98a81d9dbac212
4
+ data.tar.gz: 9479aa3c4ae7f3481a0a96baf002735fb02a4848530a9fc38565c72118e4a2eb
5
5
  SHA512:
6
- metadata.gz: 7995f2fd1718a72f5288112d3dddc51c8305ba319b5ab383712ea484095b52083d6c40ee3738f2964456919fa634f2ea10119cae814665c947d24d28d2a7a638
7
- data.tar.gz: a301b0efba48e0ce790b3743e8477dfaaca41e6c9e242f60cbd0d474acb51619eb44ec0f68bb7da00e8c5a0d73582080602d2823ab81bd4f7c686a51d6291f84
6
+ metadata.gz: 864f165ec8332dbcc0a61cffda45f6649ed00d5483e969ad7d55dc9af2e4acdbe202f54686424ff387f6f36ecd160b3834e89f4d1633a8bf311534b6c09eb0b3
7
+ data.tar.gz: f182809b76cf85e8ce23002575fb5c2708e527cd3659628d0ef248fdce79e1581ed6a551f99ca41a16dff7bacf9d67db87579185761052136e3f491d227f8bf6
data/README.md CHANGED
@@ -1,7 +1,165 @@
1
1
  # Nokodiff
2
2
 
3
- A Ruby Gem to highlight additions, deletions and character level changes while preserving original HTML
3
+ A Ruby Gem to highlight additions, deletions and character level changes while preserving original HTML.
4
+
5
+ It includes functionality to:
6
+ * Compare two HTML fragments and output diffs with semantic HTML
7
+ * Inline character differences highlighting using `<strong>` tagging
8
+ * Blocks of added or removed content wrapped in aria labelled `<ins>` and `<del>` tags
9
+ * Optional CSS for styling the visual differences
10
+
11
+ ## Installation
12
+
13
+ Install the gem:
14
+
15
+ ```
16
+ gem install nokodiff
17
+ ```
18
+
19
+ or add it to your Gemfile
20
+
21
+ ```
22
+ gem "nokodiff"
23
+ ```
24
+
25
+ ## Usage
26
+
27
+ In the controller:
28
+ ```ruby
29
+ require 'nokodiff'
30
+
31
+ before_html = < YOUR HTML >
32
+ after_html = < YOUR HTML >
33
+
34
+ @differ = Nokodiff.diff(before_html, after_html)
35
+ ```
36
+
37
+ In the erb file:
38
+ ```erb
39
+ <div>
40
+ <%= @differ %>
41
+ </div>
42
+ ```
43
+
44
+ ### Including the CSS
45
+
46
+ In your application.scss file include:
47
+ ```scss
48
+ @import "nokodiff";
49
+ ```
50
+
51
+ This will include the styling for `<del>`, `<ins>` and `<strong>` tags to allow colour coding, highlighting and underlining of changes.
52
+
53
+ ### More complex diffing with `data-diff-key`
54
+
55
+ For more complex HTML structures, a standard diff can produce surprising or misleading results when elements are added, removed, or reordered. To get around this, add a `data-diff-key` attribute to elements you want compared in isolation.
56
+
57
+ Each element with a unique `data-diff-key` value is diffed independently against its counterpart in the other HTML fragment. This prevents unrelated changes from affecting each other and produces more accurate, contextually meaningful output.
58
+
59
+ The key value can be any unique string — it just needs to match between your `before` and `after` HTML for the elements you want paired together.
60
+
61
+ #### Adding a new element
62
+
63
+ When a `data-diff-key` element is present in `after` but not in `before`, its content is wrapped in an `<ins>` tag:
64
+
65
+ ```ruby
66
+ before = <<~HTML
67
+ <div>
68
+ <div data-diff-key="ixn4">
69
+ <p>First paragraph</p>
70
+ </div>
71
+ </div>
72
+ HTML
73
+
74
+ after = <<~HTML
75
+ <div>
76
+ <div data-diff-key="ixn4">
77
+ <p>First paragraph</p>
78
+ </div>
79
+ <div data-diff-key="zm7q">
80
+ <p>Second paragraph</p>
81
+ </div>
82
+ </div>
83
+ HTML
84
+
85
+ Nokodiff.diff(before, after)
86
+ ```
87
+
88
+ Output:
89
+
90
+ ```html
91
+ <div>
92
+ <div data-diff-key="ixn4">
93
+ <p>First paragraph</p>
94
+ </div>
95
+ <div data-diff-key="zm7q">
96
+ <div class="diff">
97
+ <ins aria-label="added content"><p>Second paragraph</p></ins>
98
+ </div>
99
+ </div>
100
+ </div>
101
+ ```
102
+
103
+ ![Adding a new element](docs/adding-a-new-element.png)
104
+
105
+ #### Modifying an existing element
106
+
107
+ When a `data-diff-key` element exists in both fragments but its content has changed, the element is diffed in isolation. Character-level changes are highlighted within `<del>` and `<ins>` tags:
108
+
109
+ ```ruby
110
+ before = <<~HTML
111
+ <div>
112
+ <div data-diff-key="ixn4">
113
+ <p>First paragraph</p>
114
+ </div>
115
+ <div data-diff-key="zm7q">
116
+ <p>Second paragraph</p>
117
+ </div>
118
+ </div>
119
+ HTML
120
+
121
+ after = <<~HTML
122
+ <div>
123
+ <div data-diff-key="ixn4">
124
+ <p>First paragraph</p>
125
+ </div>
126
+ <div data-diff-key="zm7q">
127
+ <p>New paragraph</p>
128
+ </div>
129
+ </div>
130
+ HTML
131
+
132
+ Nokodiff.diff(before, after)
133
+ ```
134
+
135
+ Output:
136
+
137
+ ```html
138
+ <div>
139
+ <div data-diff-key="ixn4"><p>First paragraph</p></div>
140
+ <div data-diff-key="zm7q">
141
+ <div class="diff">
142
+ <del aria-label="removed content"><p><strong>S</strong>e<strong>cond</strong> paragraph</p></del>
143
+ </div>
144
+ <div class="diff">
145
+ <ins aria-label="added content"><p><strong>N</strong>e<strong>w</strong> paragraph</p></ins>
146
+ </div>
147
+ </div>
148
+ </div>
149
+ ```
150
+
151
+ ![Modifying an existing element](docs/modifying-an-existing-element.png)
152
+
153
+ #### Unchanged elements
154
+
155
+ If a `data-diff-key` element's content is identical in both fragments, no diff markup is added and the element is returned as-is.
156
+
157
+ #### Limitations
158
+
159
+ Currently, the gem does not support highlighting removed `data-diff-key` elements. If a keyed element is present in `before` but absent from `after`, it will not be marked as deleted in the output.
160
+
161
+ Thanks to [HTML Diff](https://html-diff.lix.dev/index.html) for inspiring this approach!
4
162
 
5
163
  ## Licence
6
164
 
7
- The gem is available as open source under the terms of the [MIT License.](https://opensource.org/license/MIT)
165
+ The gem is available as open source under the terms of the [MIT License.](https://opensource.org/license/MIT)
@@ -0,0 +1,75 @@
1
+ // stylelint-disable selector-no-qualifying-type, max-nesting-depth
2
+ // Diff of two editions
3
+ $added-color: #e6fff3;
4
+ $strong-added-color: #99ffcf;
5
+ $removed-color: #fadede;
6
+ $strong-removed-color: #f3aeac;
7
+ $text-colour: #0b0c0c;
8
+ $light-grey: #f3f2f1;
9
+ $black: #0b0c0c;
10
+
11
+ .compare-editions {
12
+ border: 1px solid $light-grey;
13
+ border-left: 40px solid $light-grey;
14
+ padding: 15px;
15
+
16
+ .diff {
17
+ margin: 0 -15px;
18
+ padding: 0 15px;
19
+ word-wrap: break-word;
20
+ margin-bottom: 10px;
21
+ position: relative;
22
+
23
+ del,
24
+ ins {
25
+ text-decoration: none;
26
+ display: block;
27
+ padding: 2px 4px;
28
+ border-radius: 3px;
29
+ }
30
+ }
31
+
32
+ del {
33
+ background-color: $removed-color;
34
+ padding-bottom: 2px;
35
+
36
+ strong {
37
+ font-weight: normal;
38
+ background-color: $strong-removed-color;
39
+ border-bottom: 2px dashed $black;
40
+ }
41
+ }
42
+
43
+ ins {
44
+ background-color: $added-color;
45
+ padding-bottom: 2px;
46
+
47
+ strong {
48
+ font-weight: normal;
49
+ background-color: $strong-added-color;
50
+ border-bottom: 2px dashed $black;
51
+ }
52
+ }
53
+
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;
62
+ }
63
+
64
+ del::before {
65
+ color: $text-colour;
66
+ background-color: $strong-removed-color;
67
+ content: "−";
68
+ }
69
+
70
+ ins::before {
71
+ color: $text-colour;
72
+ background-color: $strong-added-color;
73
+ content: "+";
74
+ }
75
+ }
@@ -0,0 +1,67 @@
1
+ module Nokodiff
2
+ class ChangesInFragments
3
+ include FormattingHelpers
4
+ def initialize(diff)
5
+ @diff = diff
6
+ @before_fragment = Nokogiri::HTML::DocumentFragment.parse("")
7
+ @after_fragment = Nokogiri::HTML::DocumentFragment.parse("")
8
+
9
+ @accumulated_before_text = ""
10
+ @accumulated_after_text = ""
11
+ end
12
+
13
+ def call
14
+ @diff.each do |change|
15
+ case change.action
16
+ when "="
17
+ no_change_emphasis(change)
18
+ when "!"
19
+ emphasise_change(change)
20
+ when "-"
21
+ emphasise_deletion(change)
22
+ when "+"
23
+ emphasise_addition(change)
24
+ end
25
+ end
26
+
27
+ append_accumulated_text(before_fragment, accumulated_before_text)
28
+ append_accumulated_text(after_fragment, accumulated_after_text)
29
+
30
+ [before_fragment, after_fragment]
31
+ end
32
+
33
+ private
34
+
35
+ attr_accessor :before_fragment, :after_fragment, :accumulated_before_text, :accumulated_after_text
36
+
37
+ def no_change_emphasis(change)
38
+ accumulated_before_text << change.old_element
39
+ accumulated_after_text << change.new_element
40
+ end
41
+
42
+ def emphasise_change(change)
43
+ append_accumulated_text(before_fragment, accumulated_before_text)
44
+ append_accumulated_text(after_fragment, accumulated_after_text)
45
+
46
+ before_fragment.add_child(wrap_in_strong(change.old_element, before_fragment))
47
+ after_fragment.add_child(wrap_in_strong(change.new_element, after_fragment))
48
+ end
49
+
50
+ def emphasise_deletion(change)
51
+ append_accumulated_text(before_fragment, accumulated_before_text)
52
+ before_fragment.add_child(wrap_in_strong(change.old_element, before_fragment))
53
+ end
54
+
55
+ def emphasise_addition(change)
56
+ append_accumulated_text(after_fragment, accumulated_after_text)
57
+ after_fragment.add_child(wrap_in_strong(change.new_element, after_fragment))
58
+ end
59
+
60
+ def append_accumulated_text(fragment, accumulated_text)
61
+ return if accumulated_text.empty?
62
+
63
+ fragment.add_child(Nokogiri::XML::Text.new(accumulated_text, fragment))
64
+ accumulated_text.clear
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,103 @@
1
+ module Nokodiff
2
+ class Differ
3
+ def initialize(before, after)
4
+ @before = before
5
+ @after = after
6
+ end
7
+
8
+ def to_html
9
+ compared_blocks.map { |diff|
10
+ case diff[:status]
11
+ when :unchanged
12
+ unchanged_block(diff[:before])
13
+ when :changed
14
+ deleted_block(char_diff_html(diff[:before], diff[:after]).first) + added_block(char_diff_html(diff[:before], diff[:after]).last)
15
+ when :deleted
16
+ deleted_block(diff[:before])
17
+ when :added
18
+ added_block(diff[:after])
19
+ end
20
+ }.join("\n")
21
+ end
22
+
23
+ private
24
+
25
+ def compared_blocks
26
+ before_nodes = @before.children.to_a
27
+ after_nodes = @after.children.to_a
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]
34
+
35
+ set_change_status(before_node, after_node)
36
+ end
37
+ end
38
+
39
+ def char_diff_html(before_html, after_html)
40
+ before_dup = before_html.dup
41
+ after_dup = after_html.dup
42
+
43
+ before_fragment, after_fragment = Nokodiff::TextNodeDiffs.new(before_dup, after_dup).call
44
+
45
+ merge_adjacent_strong_tags(before_fragment)
46
+ merge_adjacent_strong_tags(after_fragment)
47
+
48
+ [before_fragment.to_html, after_fragment.to_html]
49
+ end
50
+
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
+ def merge_adjacent_strong_tags(node)
66
+ return unless node.element?
67
+
68
+ node.children.each do |child|
69
+ merge_adjacent_strong_tags(child) if child.element?
70
+ end
71
+
72
+ node.children.each_cons(2) do |left, right|
73
+ next unless left.name == "strong" && right.name == "strong"
74
+
75
+ left.content = left.content + right.content
76
+ right.remove
77
+
78
+ merge_adjacent_strong_tags(node)
79
+ break
80
+ end
81
+ end
82
+
83
+ def unchanged_block(html)
84
+ html.to_s
85
+ end
86
+
87
+ def deleted_block(html)
88
+ %(
89
+ <div class="diff">
90
+ <del aria-label="removed content">#{html}</del>
91
+ </div>
92
+ )
93
+ end
94
+
95
+ def added_block(html)
96
+ %(
97
+ <div class="diff">
98
+ <ins aria-label="added content">#{html}</ins>
99
+ </div>
100
+ )
101
+ end
102
+ end
103
+ end
@@ -0,0 +1,7 @@
1
+ return unless defined?(Rails)
2
+
3
+ module Nokodiff
4
+ class Engine < ::Rails::Engine
5
+ isolate_namespace Nokodiff
6
+ end
7
+ end
@@ -0,0 +1,11 @@
1
+ module Nokodiff
2
+ module FormattingHelpers
3
+ def wrap_in_strong(char, fragment)
4
+ Nokogiri::XML::Node.new("strong", fragment.document).tap { |n| n.content = char }
5
+ end
6
+
7
+ def strong(text_node)
8
+ text_node.replace(wrap_in_strong(text_node.to_html, text_node.parent))
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,40 @@
1
+ require "forwardable"
2
+
3
+ module Nokodiff
4
+ class HTMLFragment
5
+ extend Forwardable
6
+
7
+ class InvalidHTMLError < StandardError; end
8
+
9
+ def initialize(html)
10
+ @fragment = Nokogiri::HTML.fragment(html)
11
+ validate!
12
+ remove_blank_nodes!
13
+ remove_comments!
14
+ end
15
+
16
+ def_delegators :@fragment, :children, :css, :at, :to_html
17
+
18
+ private
19
+
20
+ def validate!
21
+ invalid_text_nodes = @fragment.children.reject do |node|
22
+ node.element? || node.comment? || (node.text? && node.text.strip.empty?)
23
+ end
24
+
25
+ unless invalid_text_nodes.empty?
26
+ raise InvalidHTMLError, "Invalid HTML input: #{@fragment.to_html}"
27
+ end
28
+ end
29
+
30
+ def remove_blank_nodes!
31
+ @fragment.traverse do |node|
32
+ node.remove if node.blank?
33
+ end
34
+ end
35
+
36
+ def remove_comments!
37
+ @fragment.css("comment()").remove
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,56 @@
1
+ module Nokodiff
2
+ class TextNodeDiffs
3
+ include FormattingHelpers
4
+ def initialize(before_fragment, after_fragment)
5
+ @before_fragment = before_fragment
6
+ @after_fragment = after_fragment
7
+ end
8
+
9
+ def call
10
+ diff_text_nodes(before_fragment, after_fragment)
11
+ [before_fragment, after_fragment]
12
+ end
13
+
14
+ private
15
+
16
+ attr_accessor :before_fragment, :after_fragment
17
+
18
+ def diff_text_nodes(before_node, after_node)
19
+ if before_node&.text? || after_node&.text?
20
+ diff_text_node_content(before_node, after_node)
21
+ elsif before_node&.element? || after_node&.element?
22
+ before_children = before_node ? before_node.children.to_a : []
23
+ after_children = after_node ? after_node.children.to_a : []
24
+
25
+ max_child_count = [before_children.length, after_children.length].max
26
+
27
+ (0..max_child_count).each do |i|
28
+ diff_text_nodes(before_children[i], after_children[i])
29
+ end
30
+ end
31
+ end
32
+
33
+ def diff_text_node_content(before_text_node, after_text_node)
34
+ return strong(before_text_node) if text_removed?(before_text_node, after_text_node)
35
+ return strong(after_text_node) if text_added?(before_text_node, after_text_node)
36
+
37
+ before_chars = before_text_node.text.chars
38
+ after_chars = after_text_node.text.chars
39
+
40
+ diff = Diff::LCS.sdiff(before_chars, after_chars)
41
+
42
+ before_fragment, after_fragment = Nokodiff::ChangesInFragments.new(diff).call
43
+
44
+ before_text_node.replace(before_fragment)
45
+ after_text_node.replace(after_fragment)
46
+ 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
+ end
56
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Nokodiff
4
- VERSION = "0.1.0"
4
+ VERSION = "0.3.0"
5
5
  end
data/lib/nokodiff.rb CHANGED
@@ -1,8 +1,51 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "nokogiri"
4
+ require "diff-lcs"
5
+
6
+ require_relative "nokodiff/formatting_helpers"
3
7
  require_relative "nokodiff/version"
8
+ require_relative "nokodiff/differ"
9
+ require_relative "nokodiff/engine"
10
+ require_relative "nokodiff/text_node_diffs"
11
+ require_relative "nokodiff/changes_in_fragments"
12
+ require_relative "nokodiff/html_fragment"
4
13
 
5
14
  module Nokodiff
6
- class Error < StandardError; end
7
- # Your code goes here...
15
+ def self.diff(before_html, after_html)
16
+ before = Nokodiff::HTMLFragment.new(before_html)
17
+ after = Nokodiff::HTMLFragment.new(after_html)
18
+
19
+ before_nodes, after_nodes = nodes(before, after)
20
+ keys = (before_nodes.keys + after_nodes.keys).uniq
21
+
22
+ html = keys.any? ? diff_by_keys(after, keys, before_nodes, after_nodes) : Differ.new(before, after).to_html
23
+ safe_html(html)
24
+ end
25
+
26
+ def self.safe_html(html)
27
+ html.respond_to?(:html_safe) ? html.html_safe : html
28
+ end
29
+
30
+ private_class_method def self.nodes(before, after)
31
+ [
32
+ fetch_diff_nodes(before),
33
+ fetch_diff_nodes(after),
34
+ ]
35
+ end
36
+
37
+ private_class_method def self.fetch_diff_nodes(fragment)
38
+ fragment.css("[data-diff-key]").map { |node| [node["data-diff-key"], node] }.to_h
39
+ end
40
+
41
+ private_class_method def self.diff_by_keys(after, keys, before_nodes, after_nodes)
42
+ keys.each do |key|
43
+ diff = Differ.new(
44
+ before_nodes.fetch(key, Nokogiri::HTML.fragment("")),
45
+ after_nodes.fetch(key, Nokogiri::HTML.fragment("")),
46
+ ).to_html
47
+ after.at("[data-diff-key='#{key}']")&.inner_html = diff
48
+ end
49
+ after.to_html
50
+ end
8
51
  end
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.1.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - GOV.UK Dev
@@ -9,6 +9,20 @@ bindir: exe
9
9
  cert_chain: []
10
10
  date: 1980-01-02 00:00:00.000000000 Z
11
11
  dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: activesupport
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - ">="
17
+ - !ruby/object:Gem::Version
18
+ version: '0'
19
+ type: :development
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - ">="
24
+ - !ruby/object:Gem::Version
25
+ version: '0'
12
26
  - !ruby/object:Gem::Dependency
13
27
  name: rake
14
28
  requirement: !ruby/object:Gem::Requirement
@@ -57,14 +71,14 @@ dependencies:
57
71
  requirements:
58
72
  - - '='
59
73
  - !ruby/object:Gem::Version
60
- version: 5.1.20
74
+ version: 5.2.0
61
75
  type: :development
62
76
  prerelease: false
63
77
  version_requirements: !ruby/object:Gem::Requirement
64
78
  requirements:
65
79
  - - '='
66
80
  - !ruby/object:Gem::Version
67
- version: 5.1.20
81
+ version: 5.2.0
68
82
  - !ruby/object:Gem::Dependency
69
83
  name: simplecov
70
84
  requirement: !ruby/object:Gem::Requirement
@@ -88,7 +102,7 @@ dependencies:
88
102
  version: '6'
89
103
  - - "<"
90
104
  - !ruby/object:Gem::Version
91
- version: 8.1.2
105
+ version: 8.1.3
92
106
  type: :runtime
93
107
  prerelease: false
94
108
  version_requirements: !ruby/object:Gem::Requirement
@@ -98,21 +112,41 @@ dependencies:
98
112
  version: '6'
99
113
  - - "<"
100
114
  - !ruby/object:Gem::Version
101
- version: 8.1.2
115
+ version: 8.1.3
116
+ - !ruby/object:Gem::Dependency
117
+ name: diff-lcs
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'
102
130
  - !ruby/object:Gem::Dependency
103
131
  name: gds-api-adapters
104
132
  requirement: !ruby/object:Gem::Requirement
105
133
  requirements:
106
- - - "~>"
134
+ - - ">="
135
+ - !ruby/object:Gem::Version
136
+ version: '101.0'
137
+ - - "<"
107
138
  - !ruby/object:Gem::Version
108
- version: 101.0.0
139
+ version: '101.3'
109
140
  type: :runtime
110
141
  prerelease: false
111
142
  version_requirements: !ruby/object:Gem::Requirement
112
143
  requirements:
113
- - - "~>"
144
+ - - ">="
145
+ - !ruby/object:Gem::Version
146
+ version: '101.0'
147
+ - - "<"
114
148
  - !ruby/object:Gem::Version
115
- version: 101.0.0
149
+ version: '101.3'
116
150
  - !ruby/object:Gem::Dependency
117
151
  name: govspeak
118
152
  requirement: !ruby/object:Gem::Requirement
@@ -136,7 +170,7 @@ dependencies:
136
170
  version: '6'
137
171
  - - "<"
138
172
  - !ruby/object:Gem::Version
139
- version: 8.1.2
173
+ version: 8.1.3
140
174
  type: :runtime
141
175
  prerelease: false
142
176
  version_requirements: !ruby/object:Gem::Requirement
@@ -146,7 +180,7 @@ dependencies:
146
180
  version: '6'
147
181
  - - "<"
148
182
  - !ruby/object:Gem::Version
149
- version: 8.1.2
183
+ version: 8.1.3
150
184
  - !ruby/object:Gem::Dependency
151
185
  name: view_component
152
186
  requirement: !ruby/object:Gem::Requirement
@@ -169,7 +203,14 @@ extra_rdoc_files: []
169
203
  files:
170
204
  - LICENCE.txt
171
205
  - README.md
206
+ - app/assets/stylesheets/nokodiff.scss
172
207
  - lib/nokodiff.rb
208
+ - lib/nokodiff/changes_in_fragments.rb
209
+ - lib/nokodiff/differ.rb
210
+ - lib/nokodiff/engine.rb
211
+ - lib/nokodiff/formatting_helpers.rb
212
+ - lib/nokodiff/html_fragment.rb
213
+ - lib/nokodiff/text_node_diffs.rb
173
214
  - lib/nokodiff/version.rb
174
215
  homepage: https://github.com/alphagov/nokodiff
175
216
  licenses:
@@ -189,7 +230,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
189
230
  - !ruby/object:Gem::Version
190
231
  version: '0'
191
232
  requirements: []
192
- rubygems_version: 4.0.3
233
+ rubygems_version: 4.0.7
193
234
  specification_version: 4
194
235
  summary: A Ruby Gem to highlight additions, deletions and character level changes
195
236
  while preserving original HTML