levenstein_with_path 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +9 -0
- data/.rspec +2 -0
- data/.rubocop.yml +81 -0
- data/.travis.yml +16 -0
- data/.yardopts +1 -0
- data/Gemfile +2 -0
- data/LICENSE.txt +20 -0
- data/README.md +79 -0
- data/Rakefile +19 -0
- data/levenstein_with_path.gemspec +28 -0
- data/lib/levenstein_with_path.rb +139 -0
- data/lib/levenstein_with_path/version.rb +3 -0
- data/spec/levenstein_with_path_spec.rb +80 -0
- data/spec/spec_helper.rb +11 -0
- metadata +130 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 2ac652def9cc19e91d929d8564d8c149cf5a7d76bfffa64a5a1c33f4916c2da2
|
4
|
+
data.tar.gz: 8ef661698b9ac965abf7ecac94db5eb10e383660ec6404a586a966bf06b67995
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 45450cf22c284ccae072c7c6ac87318497072f15f62a113749ffc2e624b9d8bbd8c96f61955ada897ae999564aef8d00c77201a5e0288fa5925e8e4a14335f91
|
7
|
+
data.tar.gz: 32f95bf400659442b39c5ef626ed7465e1d09e3536a8190650adfc9a7e3f8c285eba63af1b5e4b21004f9fc98534f42745088ef811f4df0dddba563cfbb2ac5c
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.rubocop.yml
ADDED
@@ -0,0 +1,81 @@
|
|
1
|
+
Metrics/BlockLength:
|
2
|
+
Enabled: false
|
3
|
+
|
4
|
+
Metrics/LineLength:
|
5
|
+
Exclude:
|
6
|
+
- "Rakefile"
|
7
|
+
Max: 120
|
8
|
+
|
9
|
+
Metrics/MethodLength:
|
10
|
+
Exclude:
|
11
|
+
- "spec/**/*"
|
12
|
+
Max: 35
|
13
|
+
|
14
|
+
Metrics/AbcSize:
|
15
|
+
Max: 60
|
16
|
+
|
17
|
+
Metrics/CyclomaticComplexity:
|
18
|
+
Max: 12
|
19
|
+
|
20
|
+
Metrics/PerceivedComplexity:
|
21
|
+
Max: 15
|
22
|
+
|
23
|
+
Style/BlockDelimiters:
|
24
|
+
EnforcedStyle: line_count_based
|
25
|
+
Exclude:
|
26
|
+
- "spec/**/*"
|
27
|
+
|
28
|
+
Style/Documentation:
|
29
|
+
Exclude:
|
30
|
+
- "spec/**/*"
|
31
|
+
Style/PercentLiteralDelimiters:
|
32
|
+
PreferredDelimiters:
|
33
|
+
"%w": "[]"
|
34
|
+
|
35
|
+
Style/StringLiterals:
|
36
|
+
Enabled: false
|
37
|
+
|
38
|
+
Style/ExpandPathArguments:
|
39
|
+
Enabled: false
|
40
|
+
|
41
|
+
Gemspec/OrderedDependencies:
|
42
|
+
Enabled: false
|
43
|
+
|
44
|
+
Naming/HeredocDelimiterNaming:
|
45
|
+
Enabled: false
|
46
|
+
|
47
|
+
Layout/EmptyLinesAroundBlockBody:
|
48
|
+
Enabled: false
|
49
|
+
|
50
|
+
Layout/IndentHeredoc:
|
51
|
+
Enabled: false
|
52
|
+
|
53
|
+
Style/Encoding:
|
54
|
+
Enabled: false
|
55
|
+
|
56
|
+
Lint/AmbiguousBlockAssociation:
|
57
|
+
Enabled: false
|
58
|
+
|
59
|
+
Layout/SpaceBeforeBlockBraces:
|
60
|
+
Enabled: false
|
61
|
+
|
62
|
+
Layout/SpaceInsideBlockBraces:
|
63
|
+
Enabled: false
|
64
|
+
|
65
|
+
Style/TrailingCommaInArrayLiteral:
|
66
|
+
Enabled: false
|
67
|
+
|
68
|
+
Style/AndOr:
|
69
|
+
Enabled: false
|
70
|
+
|
71
|
+
Layout/EmptyLinesAroundModuleBody:
|
72
|
+
Enabled: false
|
73
|
+
|
74
|
+
Layout/EmptyLinesAroundClassBody:
|
75
|
+
Enabled: false
|
76
|
+
|
77
|
+
Style/ConditionalAssignment:
|
78
|
+
Enabled: false
|
79
|
+
|
80
|
+
Style/TernaryParentheses:
|
81
|
+
Enabled: false
|
data/.travis.yml
ADDED
data/.yardopts
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--markup markdown
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2018 Paul A. Jungwirth
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,79 @@
|
|
1
|
+
levenstein_with_path
|
2
|
+
====================
|
3
|
+
|
4
|
+
[![Build Status](https://travis-ci.org/pjungwir/levenstein_with_path.svg?branch=master)](https://travis-ci.org/pjungwir/levenstein_with_path)
|
5
|
+
|
6
|
+
Ruby gem to find the Levenstein distance *and its sequence of edits*
|
7
|
+
between two strings or arrays of tokens (like words, lines, etc.).
|
8
|
+
|
9
|
+
Based on the [Wagner-Fischer algorithm](https://en.wikipedia.org/wiki/Wagner%E2%80%93Fischer_algorithm),
|
10
|
+
which is `O(n^2)` but lets you retain the history of edits.
|
11
|
+
|
12
|
+
|
13
|
+
Installation
|
14
|
+
------------
|
15
|
+
|
16
|
+
Add this line to your application's Gemfile:
|
17
|
+
|
18
|
+
```ruby
|
19
|
+
gem 'levenstein_with_path'
|
20
|
+
```
|
21
|
+
|
22
|
+
And then execute:
|
23
|
+
|
24
|
+
$ bundle
|
25
|
+
|
26
|
+
Or install it yourself as:
|
27
|
+
|
28
|
+
$ gem install levenstein_with_path
|
29
|
+
|
30
|
+
|
31
|
+
Usage
|
32
|
+
-----
|
33
|
+
|
34
|
+
```ruby
|
35
|
+
p = LevensteinWithPath::Path.new('kitten', 'sitting')
|
36
|
+
p.score
|
37
|
+
# => 3
|
38
|
+
p.edits
|
39
|
+
# => [
|
40
|
+
# LevensteinWithPath::Swap.new('k', 's'),
|
41
|
+
# LevensteinWithPath::Keep.new('i'),
|
42
|
+
# LevensteinWithPath::Keep.new('t'),
|
43
|
+
# LevensteinWithPath::Keep.new('t'),
|
44
|
+
# LevensteinWithPath::Swap.new('e', 'i'),
|
45
|
+
# LevensteinWithPath::Keep.new('n'),
|
46
|
+
# LevensteinWithPath::Insert.new('g'),
|
47
|
+
# ]
|
48
|
+
```
|
49
|
+
|
50
|
+
|
51
|
+
Tests
|
52
|
+
-----
|
53
|
+
|
54
|
+
Say `rake` to run rspec tests plus Rubocop,
|
55
|
+
or `rspec spec` to run just the tests.
|
56
|
+
|
57
|
+
|
58
|
+
Contributing
|
59
|
+
------------
|
60
|
+
|
61
|
+
* Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet.
|
62
|
+
* Check out the issue tracker to make sure someone hasn't already requested and/or contributed it.
|
63
|
+
* Fork the project.
|
64
|
+
* Start a feature/bugfix branch.
|
65
|
+
* Commit and push until you are happy with your contribution.
|
66
|
+
* Make be sure to add tests for it. This is important so I don't break it in a future version unintentionally.
|
67
|
+
* Please try not to mess with the Rakefile, version, or history. If you want to have your own version, that is fine, but please isolate that change to its own commit so I can cherry-pick around it.
|
68
|
+
|
69
|
+
Commands for building/releasing/installing:
|
70
|
+
|
71
|
+
* `rake build`
|
72
|
+
* `rake install`
|
73
|
+
* `rake release`
|
74
|
+
|
75
|
+
Copyright
|
76
|
+
---------
|
77
|
+
|
78
|
+
Copyright (c) 2018 Paul A. Jungwirth.
|
79
|
+
See LICENSE.txt for further details.
|
data/Rakefile
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'bundler/gem_tasks'
|
2
|
+
require 'rspec/core/rake_task'
|
3
|
+
require 'rubocop/rake_task'
|
4
|
+
|
5
|
+
RSpec::Core::RakeTask.new(:spec)
|
6
|
+
RuboCop::RakeTask.new
|
7
|
+
|
8
|
+
task readme: [] do
|
9
|
+
system %q{jq --slurp --raw-input '{"text": "\(.)", "mode": "gfm", "context": "pjungwir/levenstein_with_path"}' < README.md | curl --data @- https://api.github.com/markdown > README.html}
|
10
|
+
end
|
11
|
+
|
12
|
+
if ENV['CI'].nil?
|
13
|
+
task default: %w[spec rubocop]
|
14
|
+
else
|
15
|
+
case ENV['SUITE']
|
16
|
+
when 'rubocop' then task default: :rubocop
|
17
|
+
else task default: :spec
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
lib = File.expand_path('../lib', __FILE__)
|
2
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
3
|
+
require 'levenstein_with_path/version'
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = 'levenstein_with_path'
|
7
|
+
s.version = LevensteinWithPath::VERSION
|
8
|
+
|
9
|
+
s.summary = 'Computes Levenstein distance between arrays of tokens, and gives the list of edits'
|
10
|
+
s.description = 'Computes Levenstein distance between arrays of tokens, and gives the list of edits'
|
11
|
+
|
12
|
+
s.authors = ['Paul A. Jungwirth']
|
13
|
+
s.homepage = 'https://github.com/pjungwir/levenstein_with_path'
|
14
|
+
s.email = ['pj@illuminatedcomputing.com']
|
15
|
+
|
16
|
+
s.licenses = ['MIT']
|
17
|
+
|
18
|
+
s.require_paths = ['lib']
|
19
|
+
s.executables = []
|
20
|
+
s.files = `git ls-files`.split("\n")
|
21
|
+
s.test_files = `git ls-files -- {test,spec,fixtures}/*`.split("\n")
|
22
|
+
|
23
|
+
s.add_development_dependency 'rspec', '>= 3.2.0'
|
24
|
+
s.add_development_dependency 'rake'
|
25
|
+
s.add_development_dependency 'bundler'
|
26
|
+
s.add_development_dependency 'simplecov'
|
27
|
+
s.add_development_dependency 'rubocop'
|
28
|
+
end
|
@@ -0,0 +1,139 @@
|
|
1
|
+
module LevensteinWithPath
|
2
|
+
|
3
|
+
# Computes the levenstein distance between two sequences.
|
4
|
+
#
|
5
|
+
# Unlike other Ruby libraries, this one:
|
6
|
+
#
|
7
|
+
# - Can tell you not just the optimal score, but the sequence of operations to get it.
|
8
|
+
# This is useful if you want to present a nice diff.
|
9
|
+
# - Lets you pass either strings or array of some other atomic tokens
|
10
|
+
# (like words, lines, nucleobases, etc.).
|
11
|
+
#
|
12
|
+
# It is based on the [Wagner-Fischer algorithm](https://en.wikipedia.org/wiki/Wagner%E2%80%93Fischer_algorithm),
|
13
|
+
# which is `O(n^2)` but lets you retain the history of edits.
|
14
|
+
# See also [that algorithm applied to Levenstein distance](https://en.wikipedia.org/wiki/Levenshtein_distance#Computing_Levenshtein_distance).
|
15
|
+
class Path
|
16
|
+
attr_reader :tokens1, :tokens2
|
17
|
+
attr_reader :scores, :score
|
18
|
+
|
19
|
+
def initialize(tokens1, tokens2)
|
20
|
+
@tokens1 = if tokens1.is_a?(String)
|
21
|
+
tokens1.chars
|
22
|
+
else
|
23
|
+
tokens1
|
24
|
+
end.to_a
|
25
|
+
@tokens2 = if tokens2.is_a?(String)
|
26
|
+
tokens2.chars
|
27
|
+
else
|
28
|
+
tokens2
|
29
|
+
end.to_a
|
30
|
+
|
31
|
+
s = @tokens1
|
32
|
+
t = @tokens2
|
33
|
+
m = s.size
|
34
|
+
n = t.size
|
35
|
+
d = 0.upto(m).map { [0] * (n + 1)}
|
36
|
+
1.upto(m) {|i| d[i][0] = i}
|
37
|
+
1.upto(n) {|j| d[0][j] = j}
|
38
|
+
|
39
|
+
# rubocop:disable Layout/SpaceInsideReferenceBrackets
|
40
|
+
1.upto(n) do |j|
|
41
|
+
1.upto(m) do |i|
|
42
|
+
cost = s[i - 1] == t[j - 1] ? 0 : 1
|
43
|
+
d[i][j] = [
|
44
|
+
d[i - 1][j ] + 1, # delete
|
45
|
+
d[i ][j - 1] + 1, # insert
|
46
|
+
d[i - 1][j - 1] + cost # keep/swap
|
47
|
+
].min
|
48
|
+
end
|
49
|
+
end
|
50
|
+
# rubocop:enable Layout/SpaceInsideReferenceBrackets
|
51
|
+
|
52
|
+
@scores = d
|
53
|
+
@score = d[m][n]
|
54
|
+
end
|
55
|
+
|
56
|
+
def edits
|
57
|
+
return @edits if @edits
|
58
|
+
@edits = []
|
59
|
+
s = @tokens1
|
60
|
+
t = @tokens2
|
61
|
+
m = s.size
|
62
|
+
n = t.size
|
63
|
+
i = m
|
64
|
+
j = n
|
65
|
+
d = @scores
|
66
|
+
while i > 0 or j > 0
|
67
|
+
cur_score = d[i][j]
|
68
|
+
insert_score = j > 0 ? d[i][j - 1] : cur_score + 1
|
69
|
+
delete_score = i > 0 ? d[i - 1][j] : cur_score + 1
|
70
|
+
keep_or_swap_score = (i > 0 and j > 0) ? d[i - 1][j - 1] : cur_score + 1
|
71
|
+
best_score = [insert_score, delete_score, keep_or_swap_score].min
|
72
|
+
if best_score == keep_or_swap_score
|
73
|
+
if cur_score == keep_or_swap_score
|
74
|
+
@edits << Keep.new(s[i - 1])
|
75
|
+
else
|
76
|
+
@edits << Swap.new(s[i - 1], t[j - 1])
|
77
|
+
end
|
78
|
+
i -= 1
|
79
|
+
j -= 1
|
80
|
+
elsif best_score == insert_score
|
81
|
+
@edits << Insert.new(t[j - 1])
|
82
|
+
j -= 1
|
83
|
+
else
|
84
|
+
@edits << Delete.new(s[i - 1])
|
85
|
+
i -= 1
|
86
|
+
end
|
87
|
+
end
|
88
|
+
@edits.reverse!
|
89
|
+
end
|
90
|
+
|
91
|
+
end
|
92
|
+
|
93
|
+
# rubocop:disable Layout/EmptyLineBetweenDefs
|
94
|
+
class Edit
|
95
|
+
end
|
96
|
+
|
97
|
+
class Keep < Edit #:nodoc:
|
98
|
+
attr_reader :token
|
99
|
+
def initialize(token)
|
100
|
+
@token = token
|
101
|
+
end
|
102
|
+
def ==(other)
|
103
|
+
other.is_a?(Keep) and other.token == token
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
class Delete < Edit #:nodoc:
|
108
|
+
attr_reader :token
|
109
|
+
def initialize(token)
|
110
|
+
@token = token
|
111
|
+
end
|
112
|
+
def ==(other)
|
113
|
+
other.is_a?(Delete) and other.token == token
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
class Insert < Edit #:nodoc:
|
118
|
+
attr_reader :token
|
119
|
+
def initialize(token)
|
120
|
+
@token = token
|
121
|
+
end
|
122
|
+
def ==(other)
|
123
|
+
other.is_a?(Edit) and other.token == token
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
class Swap < Edit #:nodoc:
|
128
|
+
attr_reader :token1, :token2
|
129
|
+
def initialize(token1, token2)
|
130
|
+
@token1 = token1
|
131
|
+
@token2 = token2
|
132
|
+
end
|
133
|
+
def ==(other)
|
134
|
+
other.is_a?(Swap) and other.token1 == token1 and other.token2 == token2
|
135
|
+
end
|
136
|
+
end
|
137
|
+
# rubocop:enable Layout/EmptyLineBetweenDefs
|
138
|
+
|
139
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
describe 'LevensteinWithPath' do
|
4
|
+
# Some real tests:
|
5
|
+
let(:kitten_to_sitting) { LevensteinWithPath::Path.new('kitten', 'sitting') }
|
6
|
+
let(:saturday_to_sunday) { LevensteinWithPath::Path.new('Saturday', 'Sunday') }
|
7
|
+
|
8
|
+
# Non-ascii:
|
9
|
+
let(:eye_to_navel) { LevensteinWithPath::Path.new("ὀφθαλμός", "ὀμφαλός") }
|
10
|
+
|
11
|
+
# Some degenerate cases:
|
12
|
+
let(:cat_to_empty) { LevensteinWithPath::Path.new('cat', '') }
|
13
|
+
let(:empty_to_cat) { LevensteinWithPath::Path.new('', 'cat') }
|
14
|
+
let(:empty_to_empty) { LevensteinWithPath::Path.new('', '') }
|
15
|
+
let(:a_to_a) { LevensteinWithPath::Path.new('a', 'a') }
|
16
|
+
let(:cat_to_cat) { LevensteinWithPath::Path.new('cat', 'cat') }
|
17
|
+
|
18
|
+
# Arrays of words:
|
19
|
+
let(:vita_ad_spes) { LevensteinWithPath::Path.new(%w[Dum vita est], %w[spes est]) }
|
20
|
+
|
21
|
+
it "computes the score" do
|
22
|
+
expect(kitten_to_sitting.score).to eq 3
|
23
|
+
expect(saturday_to_sunday.score).to eq 3
|
24
|
+
|
25
|
+
expect(eye_to_navel.score).to eq 3
|
26
|
+
|
27
|
+
expect(cat_to_empty.score).to eq 3
|
28
|
+
expect(empty_to_cat.score).to eq 3
|
29
|
+
expect(empty_to_empty.score).to eq 0
|
30
|
+
expect(a_to_a.score).to eq 0
|
31
|
+
expect(cat_to_cat.score).to eq 0
|
32
|
+
|
33
|
+
expect(vita_ad_spes.score).to eq 2
|
34
|
+
end
|
35
|
+
|
36
|
+
it "lists the edits" do
|
37
|
+
expect(kitten_to_sitting.edits).to eq [
|
38
|
+
LevensteinWithPath::Swap.new('k', 's'),
|
39
|
+
LevensteinWithPath::Keep.new('i'),
|
40
|
+
LevensteinWithPath::Keep.new('t'),
|
41
|
+
LevensteinWithPath::Keep.new('t'),
|
42
|
+
LevensteinWithPath::Swap.new('e', 'i'),
|
43
|
+
LevensteinWithPath::Keep.new('n'),
|
44
|
+
LevensteinWithPath::Insert.new('g'),
|
45
|
+
]
|
46
|
+
expect(saturday_to_sunday.edits).to eq [
|
47
|
+
LevensteinWithPath::Keep.new('S'),
|
48
|
+
LevensteinWithPath::Delete.new('a'),
|
49
|
+
LevensteinWithPath::Delete.new('t'),
|
50
|
+
LevensteinWithPath::Keep.new('u'),
|
51
|
+
LevensteinWithPath::Swap.new('r', 'n'),
|
52
|
+
LevensteinWithPath::Keep.new('d'),
|
53
|
+
LevensteinWithPath::Keep.new('a'),
|
54
|
+
LevensteinWithPath::Keep.new('y'),
|
55
|
+
]
|
56
|
+
|
57
|
+
expect(eye_to_navel.edits).to eq [
|
58
|
+
LevensteinWithPath::Keep.new('ὀ'),
|
59
|
+
LevensteinWithPath::Swap.new('φ', 'μ'),
|
60
|
+
LevensteinWithPath::Swap.new('θ', 'φ'),
|
61
|
+
LevensteinWithPath::Keep.new('α'),
|
62
|
+
LevensteinWithPath::Keep.new('λ'),
|
63
|
+
LevensteinWithPath::Delete.new('μ'),
|
64
|
+
LevensteinWithPath::Keep.new('ό'),
|
65
|
+
LevensteinWithPath::Keep.new('ς'),
|
66
|
+
]
|
67
|
+
|
68
|
+
expect(cat_to_empty.edits).to eq %w[c a t].map{|x| LevensteinWithPath::Delete.new(x)}
|
69
|
+
expect(empty_to_cat.edits).to eq %w[c a t].map{|x| LevensteinWithPath::Insert.new(x)}
|
70
|
+
expect(empty_to_empty.edits).to eq []
|
71
|
+
expect(a_to_a.edits).to eq [LevensteinWithPath::Keep.new('a')]
|
72
|
+
expect(cat_to_cat.edits).to eq %w[c a t].map{|x| LevensteinWithPath::Keep.new(x)}
|
73
|
+
|
74
|
+
expect(vita_ad_spes.edits).to eq [
|
75
|
+
LevensteinWithPath::Delete.new('Dum'),
|
76
|
+
LevensteinWithPath::Swap.new('vita', 'spes'),
|
77
|
+
LevensteinWithPath::Keep.new('est'),
|
78
|
+
]
|
79
|
+
end
|
80
|
+
end
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,130 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: levenstein_with_path
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Paul A. Jungwirth
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2018-07-21 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rspec
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 3.2.0
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 3.2.0
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: bundler
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: simplecov
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rubocop
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
description: Computes Levenstein distance between arrays of tokens, and gives the
|
84
|
+
list of edits
|
85
|
+
email:
|
86
|
+
- pj@illuminatedcomputing.com
|
87
|
+
executables: []
|
88
|
+
extensions: []
|
89
|
+
extra_rdoc_files: []
|
90
|
+
files:
|
91
|
+
- ".gitignore"
|
92
|
+
- ".rspec"
|
93
|
+
- ".rubocop.yml"
|
94
|
+
- ".travis.yml"
|
95
|
+
- ".yardopts"
|
96
|
+
- Gemfile
|
97
|
+
- LICENSE.txt
|
98
|
+
- README.md
|
99
|
+
- Rakefile
|
100
|
+
- levenstein_with_path.gemspec
|
101
|
+
- lib/levenstein_with_path.rb
|
102
|
+
- lib/levenstein_with_path/version.rb
|
103
|
+
- spec/levenstein_with_path_spec.rb
|
104
|
+
- spec/spec_helper.rb
|
105
|
+
homepage: https://github.com/pjungwir/levenstein_with_path
|
106
|
+
licenses:
|
107
|
+
- MIT
|
108
|
+
metadata: {}
|
109
|
+
post_install_message:
|
110
|
+
rdoc_options: []
|
111
|
+
require_paths:
|
112
|
+
- lib
|
113
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - ">="
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
119
|
+
requirements:
|
120
|
+
- - ">="
|
121
|
+
- !ruby/object:Gem::Version
|
122
|
+
version: '0'
|
123
|
+
requirements: []
|
124
|
+
rubyforge_project:
|
125
|
+
rubygems_version: 2.7.6
|
126
|
+
signing_key:
|
127
|
+
specification_version: 4
|
128
|
+
summary: Computes Levenstein distance between arrays of tokens, and gives the list
|
129
|
+
of edits
|
130
|
+
test_files: []
|