rblines 0.1.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 22cf08f778fdfeb780fa865d66f160b79f77370a45fed45dec205eb767559903
4
+ data.tar.gz: 993997a366cafa54ab7635eb5902ca6b122bf3704ff260ef0d3088c592b9792c
5
+ SHA512:
6
+ metadata.gz: a54dd686a0799c6520706b4ba25fe79d7e7d8e94484cda6fd0d14de022f4fe01fd5d78dca1505d031fadc6475a1e73990497299f7900dc0b72ce1e221ad2ea4b
7
+ data.tar.gz: 7357638628ff3cf9be0a465e8839a4202d2456f5e69fda76ed4219d75be274a1727cc11edb5c793aeced7c632c6e614385c985c11eb025db251297ad27269e33
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
data/.rubocop.yml ADDED
@@ -0,0 +1,8 @@
1
+ require:
2
+ - standard
3
+ - rubocop-performance
4
+
5
+ inherit_gem:
6
+ standard: config/base.yml
7
+ standard-performance: config/base.yml
8
+ standard-custom: config/base.yml
data/.standard.yml ADDED
@@ -0,0 +1,3 @@
1
+ # For available configuration options, see:
2
+ # https://github.com/testdouble/standard
3
+ ruby_version: 2.6
data/.tool-versions ADDED
@@ -0,0 +1 @@
1
+ ruby 3.2.2
data/CHANGELOG.md ADDED
@@ -0,0 +1,5 @@
1
+ ## [Unreleased]
2
+
3
+ ## [0.1.0] - 2023-06-05
4
+
5
+ - Initial release
data/Gemfile ADDED
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ source "https://rubygems.org"
4
+
5
+ # Specify your gem's dependencies in rblines.gemspec
6
+ gemspec
7
+
8
+ gem "rake", "~> 13.0"
9
+
10
+ gem "rspec", "~> 3.0"
11
+
12
+ gem "standard", "~> 1.3"
data/Gemfile.lock ADDED
@@ -0,0 +1,74 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ rblines (0.1.0)
5
+ diff-lcs (~> 1.5.0)
6
+
7
+ GEM
8
+ remote: https://rubygems.org/
9
+ specs:
10
+ ast (2.4.2)
11
+ diff-lcs (1.5.0)
12
+ json (2.6.3)
13
+ language_server-protocol (3.17.0.3)
14
+ lint_roller (1.0.0)
15
+ parallel (1.23.0)
16
+ parser (3.2.2.1)
17
+ ast (~> 2.4.1)
18
+ rainbow (3.1.1)
19
+ rake (13.0.6)
20
+ regexp_parser (2.8.0)
21
+ rexml (3.2.5)
22
+ rspec (3.12.0)
23
+ rspec-core (~> 3.12.0)
24
+ rspec-expectations (~> 3.12.0)
25
+ rspec-mocks (~> 3.12.0)
26
+ rspec-core (3.12.2)
27
+ rspec-support (~> 3.12.0)
28
+ rspec-expectations (3.12.3)
29
+ diff-lcs (>= 1.2.0, < 2.0)
30
+ rspec-support (~> 3.12.0)
31
+ rspec-mocks (3.12.5)
32
+ diff-lcs (>= 1.2.0, < 2.0)
33
+ rspec-support (~> 3.12.0)
34
+ rspec-support (3.12.0)
35
+ rubocop (1.52.0)
36
+ json (~> 2.3)
37
+ parallel (~> 1.10)
38
+ parser (>= 3.2.0.0)
39
+ rainbow (>= 2.2.2, < 4.0)
40
+ regexp_parser (>= 1.8, < 3.0)
41
+ rexml (>= 3.2.5, < 4.0)
42
+ rubocop-ast (>= 1.28.0, < 2.0)
43
+ ruby-progressbar (~> 1.7)
44
+ unicode-display_width (>= 2.4.0, < 3.0)
45
+ rubocop-ast (1.29.0)
46
+ parser (>= 3.2.1.0)
47
+ rubocop-performance (1.18.0)
48
+ rubocop (>= 1.7.0, < 2.0)
49
+ rubocop-ast (>= 0.4.0)
50
+ ruby-progressbar (1.13.0)
51
+ standard (1.29.0)
52
+ language_server-protocol (~> 3.17.0.2)
53
+ lint_roller (~> 1.0)
54
+ rubocop (~> 1.52.0)
55
+ standard-custom (~> 1.0.0)
56
+ standard-performance (~> 1.1.0)
57
+ standard-custom (1.0.1)
58
+ lint_roller (~> 1.0)
59
+ standard-performance (1.1.0)
60
+ lint_roller (~> 1.0)
61
+ rubocop-performance (~> 1.18.0)
62
+ unicode-display_width (2.4.2)
63
+
64
+ PLATFORMS
65
+ x86_64-linux
66
+
67
+ DEPENDENCIES
68
+ rake (~> 13.0)
69
+ rblines!
70
+ rspec (~> 3.0)
71
+ standard (~> 1.3)
72
+
73
+ BUNDLED WITH
74
+ 2.4.10
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2023 Kadu Diógenes
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,83 @@
1
+ # Rblines
2
+
3
+ Rblines is a Ruby clone of the [Redlines](https://github.com/houfu/redlines) Python package. It produces a Markdown text showing the differences between two strings/text. The changes are represented with strike-throughs and underlines, which looks similar to Microsoft Word's track changes. This method of showing changes is more familiar to lawyers and is more compact for long series of characters.
4
+
5
+ ## Example
6
+
7
+ Given an original string:
8
+
9
+ The quick brown fox jumps over the lazy dog.
10
+
11
+ And the string to be tested with:
12
+
13
+ The quick brown fox walks past the lazy dog.
14
+
15
+ The library gives a result of:
16
+
17
+ The quick brown fox <del>jumps over </del><ins>walks past </ins>the lazy dog.
18
+
19
+ Which is rendered like this:
20
+
21
+ The quick brown fox <del>jumps over </del><ins>walks past </ins>the lazy dog.
22
+
23
+ ## Installation
24
+
25
+ Install the gem and add to the application's Gemfile by executing:
26
+
27
+ $ bundle add rblines
28
+
29
+ If bundler is not being used to manage dependencies, install the gem by executing:
30
+
31
+ $ gem install rblines
32
+
33
+ ## Usage
34
+
35
+ The library contains one class: `Rblines::Redlines`, which is used to compare text.
36
+
37
+ ```ruby
38
+ test = Rblines::Redlines.new(
39
+ "The quick brown fox jumps over the lazy dog.",
40
+ "The quick brown fox walks past the lazy dog.",
41
+ markdown_style: "none"
42
+ )
43
+ test.output_markdown == "The quick brown fox <del>jumps over </del><ins>walks past </ins>the lazy dog."
44
+ # => True
45
+ ```
46
+
47
+ Alternatively, you can create Redline with the text to be tested, and compare several times to see the results.
48
+
49
+ ```ruby
50
+ test = Rblines::Redlines.new("The quick brown fox jumps over the lazy dog.", markdown_style: "none")
51
+ test.compare("The quick brown fox walks past the lazy dog.") == "The quick brown fox <del>jumps over </del><ins>walks past </ins>the lazy dog."
52
+ # => True
53
+
54
+ test.compare("The quick brown fox jumps over the dog.") == "The quick brown fox jumps over the <del>lazy </del>dog."
55
+ # => True
56
+ ```
57
+
58
+ ## Development
59
+
60
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
61
+
62
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
63
+
64
+ ## Know Issues
65
+
66
+ The original [Redlines](https://github.com/houfu/redlines) uses the `SequenceMatcher` from [difflib](https://github.com/python/cpython/blob/main/Lib/difflib.py). This handles the diff a bit different from the gem [diff-lcs](https://github.com/halostatue/diff-lcs) that we are using in this project. So in the spec `handles different number of paragraphs correctly` we changed the expected output a bit:
67
+
68
+ ```ruby
69
+ # changed the expected output from:
70
+ expected_md = "Happy Saturday, \n\nThank you for reaching <del>out, have </del><ins>out. Have </ins>a good <del>weekend \n\nBest, ¶ Sophia</del><ins>weekend. \n\nSophia.</ins>"
71
+ # to:
72
+ expected_md = "Happy Saturday, \n\nThank you for reaching <del>out, have </del><ins>out. Have </ins>a good <del>weekend </del><ins>weekend. </ins>\n\n<del>Best, </del><ins>Sophia.</ins><del>¶ Sophia</del>"
73
+ ```
74
+
75
+ We think that the Redlines output is better, but until we find a better way to handle this, we will keep the current output, that is also not that bad.
76
+
77
+ ## Contributing
78
+
79
+ Bug reports and pull requests are welcome on GitHub at https://github.com/kdiogenes/rblines.
80
+
81
+ ## License
82
+
83
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "rspec/core/rake_task"
5
+
6
+ RSpec::Core::RakeTask.new(:spec)
7
+
8
+ require "standard/rake"
9
+
10
+ task default: %i[spec standard]
@@ -0,0 +1,152 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "strscan"
4
+ require "diff/lcs"
5
+
6
+ # This regular expression matches a group of characters that can include any character except for parentheses
7
+ # and whitespace characters (which include spaces, tabs, and line breaks) or any character
8
+ # that is a parenthesis or punctuation mark (.?!-).
9
+ # The group can also include any whitespace characters that follow these characters.
10
+ # Breaking it down further:
11
+ # - ( and ) indicate a capturing group
12
+ # - (?: ) is a non-capturing group, meaning it matches the pattern but doesn't capture the matched text
13
+ # - [^()\s]+ matches one or more characters that are not parentheses or whitespace characters
14
+ # - | indicates an alternative pattern
15
+ # - [().?!-] matches any character that is a parenthesis or punctuation mark (.?!-)
16
+ # - \s* matches zero or more whitespace characters (spaces, tabs, or line breaks) that follow the previous pattern.
17
+ TOKENIZER = /((?:[^()\s]+|[().?!-])\s*)/
18
+
19
+ # This pattern matches one or more newline characters `\n`, and any spaces between them.
20
+ # It is used to split the text into paragraphs.
21
+ # - (?:\n *) is a non-capturing group that must start with a \n and be followed by zero or more spaces.
22
+ # - ((?:\n *)+) is the previous non-capturing group repeated one or more times.
23
+ PARAGRAPH_PATTERN = /((?:\n *)+)/
24
+
25
+ SPACE_PATTERN = /(\s+)/
26
+
27
+ # Tokenizes the text based on the TOKENIZER pattern.
28
+ #
29
+ # @param text [String] the text to be tokenized
30
+ # @return [Array<String>] an array of tokenized words
31
+ def tokenize_text(text)
32
+ text.scan(TOKENIZER).flatten
33
+ end
34
+
35
+ # Splits a string into a list of paragraphs. One or more `\n` splits the paragraphs.
36
+ # For example, if the text is "Hello\nWorld\nThis is a test", the result will be:
37
+ # ['Hello', 'World', 'This is a test']
38
+ #
39
+ # @param text [String] the text to split
40
+ # @return [Array<String>] a list of paragraphs
41
+ def split_paragraphs(text)
42
+ text.split(PARAGRAPH_PATTERN)
43
+ .map(&:strip)
44
+ .reject(&:empty?)
45
+ end
46
+
47
+ # Split paragraphs and concatenate them. Then add a character '¶' between paragraphs.
48
+ # For example, if the text is "Hello\nWorld\nThis is a test", the result will be:
49
+ # "Hello¶World¶This is a test"
50
+ #
51
+ # @param text [String] the text to split
52
+ # @return [String] a string with paragraphs separated by '¶'
53
+ def concatenate_paragraphs_and_add_chr182(text)
54
+ split_paragraphs(text).join(" ¶ ")
55
+ end
56
+
57
+ module Rblines
58
+ # The Redlines class is used to compare two texts and generate a markdown output highlighting the differences between
59
+ # them.
60
+ #
61
+ # @example
62
+ # redlines = Rblines::Redlines.new("source text", "test text")
63
+ # result = redlines.output_markdown
64
+ class Redlines
65
+ MD_STYLES = {
66
+ "none" => {"ins" => %w[ins ins], "del" => %w[del del]},
67
+ "red" => {
68
+ "ins" => ['span style="color:red;font-weight:700;"', "span"],
69
+ "del" => ['span style="color:red;font-weight:700;text-decoration:line-through;"', "span"]
70
+ }
71
+ }.freeze
72
+
73
+ attr_accessor :options
74
+ attr_reader :source, :test
75
+
76
+ def source=(value)
77
+ @source = value
78
+ @seq1 = tokenize_text(concatenate_paragraphs_and_add_chr182(value))
79
+ end
80
+
81
+ def test=(value)
82
+ @test = value
83
+ @seq2 = tokenize_text(concatenate_paragraphs_and_add_chr182(value))
84
+ end
85
+
86
+ def initialize(source, test = nil, **options)
87
+ self.source = source
88
+ self.options = options
89
+ self.test = test if test
90
+ end
91
+
92
+ def opcodes
93
+ raise "No test string was provided when the function was called, or during initialisation." if @seq2.nil?
94
+
95
+ Diff::LCS.sdiff(@seq1, @seq2)
96
+ end
97
+
98
+ def output_markdown
99
+ result = []
100
+ style = MD_STYLES[options[:markdown_style] || "red"]
101
+ grouped_opcodes = opcodes.chunk_while { |a, b| a.action == b.action }.to_a
102
+
103
+ grouped_opcodes.each do |group|
104
+ group_action = group[0].action
105
+ case group_action
106
+ when "="
107
+ handle_equal_action(result, group)
108
+ when "+"
109
+ handle_add_action(result, group, style)
110
+ when "-"
111
+ handle_delete_action(result, group, style)
112
+ when "!"
113
+ handle_replace_action(result, group, style)
114
+ end
115
+ end
116
+
117
+ result.join
118
+ end
119
+
120
+ def handle_equal_action(result, group)
121
+ result.push(group.map(&:old_element).join.gsub("¶ ", "\n\n"))
122
+ end
123
+
124
+ def handle_add_action(result, group, md_styles)
125
+ temp_str = group.map(&:new_element).join.split("¶ ")
126
+ temp_str.each { |split| result.push("<#{md_styles["ins"][0]}>#{split}</#{md_styles["ins"][1]}>", "\n\n") }
127
+ result.pop if temp_str.length.positive?
128
+ end
129
+
130
+ def handle_delete_action(result, group, md_styles)
131
+ result.push("<#{md_styles["del"][0]}>#{group.map(&:old_element).join}</#{md_styles["del"][1]}>")
132
+ end
133
+
134
+ def handle_replace_action(result, group, md_styles)
135
+ result.push("<#{md_styles["del"][0]}>#{group.map(&:old_element).join}</#{md_styles["del"][1]}>")
136
+ temp_str = group.map(&:new_element).join.split("¶ ")
137
+ temp_str.each { |split| result.push("<#{md_styles["ins"][0]}>#{split}</#{md_styles["ins"][1]}>", "\n\n") }
138
+ result.pop if temp_str.length.positive?
139
+ end
140
+
141
+ def compare(test = nil, output = "markdown", options = {})
142
+ self.test = test if test
143
+ raise "No test string was provided when the function was called, or during initialisation." if self.test.nil?
144
+
145
+ self.options.merge!(options)
146
+
147
+ return unless output == "markdown"
148
+
149
+ output_markdown
150
+ end
151
+ end
152
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rblines
4
+ VERSION = "0.1.0"
5
+ end
data/lib/rblines.rb ADDED
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "rblines/version"
4
+ require_relative "rblines/redlines"
5
+
6
+ module Rblines
7
+ class Error < StandardError; end
8
+ end
data/sig/rblines.rbs ADDED
@@ -0,0 +1,4 @@
1
+ module Rblines
2
+ VERSION: String
3
+ # See the writing guide of rbs: https://github.com/ruby/rbs#guides
4
+ end
metadata ADDED
@@ -0,0 +1,77 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rblines
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Kadu Diógenes
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2023-06-06 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: diff-lcs
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 1.5.0
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 1.5.0
27
+ description: Rblines produces a Markdown text showing the differences between two
28
+ strings/text. Clone of https://github.com/houfu/redlines.
29
+ email:
30
+ - kadu@fnix.com.br
31
+ executables: []
32
+ extensions: []
33
+ extra_rdoc_files: []
34
+ files:
35
+ - ".rspec"
36
+ - ".rubocop.yml"
37
+ - ".standard.yml"
38
+ - ".tool-versions"
39
+ - CHANGELOG.md
40
+ - Gemfile
41
+ - Gemfile.lock
42
+ - LICENSE.txt
43
+ - README.md
44
+ - Rakefile
45
+ - lib/rblines.rb
46
+ - lib/rblines/redlines.rb
47
+ - lib/rblines/version.rb
48
+ - sig/rblines.rbs
49
+ homepage: https://github.com/kdiogenes/rblines
50
+ licenses:
51
+ - MIT
52
+ metadata:
53
+ allowed_push_host: https://rubygems.org
54
+ homepage_uri: https://github.com/kdiogenes/rblines
55
+ source_code_uri: https://github.com/kdiogenes/rblines
56
+ changelog_uri: https://github.com/kdiogenes/rblines/blob/master/CHANGELOG.md
57
+ post_install_message:
58
+ rdoc_options: []
59
+ require_paths:
60
+ - lib
61
+ required_ruby_version: !ruby/object:Gem::Requirement
62
+ requirements:
63
+ - - ">="
64
+ - !ruby/object:Gem::Version
65
+ version: 2.6.0
66
+ required_rubygems_version: !ruby/object:Gem::Requirement
67
+ requirements:
68
+ - - ">="
69
+ - !ruby/object:Gem::Version
70
+ version: '0'
71
+ requirements: []
72
+ rubygems_version: 3.4.10
73
+ signing_key:
74
+ specification_version: 4
75
+ summary: Rblines produces a Markdown text showing the differences between two strings/text.
76
+ Clone of https://github.com/houfu/redlines.
77
+ test_files: []