edits 0.3.0 → 0.5.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: 1c829d27a322f8c9b3ad50437a3b66d2548ccfc40d435ebaee4c362b22802fb8
4
- data.tar.gz: dd351b715125154bd7f9b37b98fd1f75aea504163fe550795c0615af1fea9b65
3
+ metadata.gz: e464255bd53bdeea1b7a599d4a8463518a46d4b836fca0a65be8148de7e793db
4
+ data.tar.gz: b65f244baa43185798e50bd0ee486cb40e923e200242d15f5c59ba2df02e0eec
5
5
  SHA512:
6
- metadata.gz: 8d5b3cd301621b913768f63c50f4876a340f620c22c2fbe68d1659bd1566141f6a737264dfb3da15f0b7a7e1a567a1036dcd978cd9c2001210f843a1e5da8f93
7
- data.tar.gz: 821c4cd4acf9e2b9e25c21ce2f61a4f489838c4252fd6c4e0fd323fe9b5f3baaa1948e3ab87b0c1448830e39c9ff2362aa457e4fe9ce902994545dd8c986137e
6
+ metadata.gz: e3ee90cc881ae7e73affb987052c3bc542e68c9e3538babd25f72c38d22adf76e6eda178ee8fa2b837b06cc84b610faa2695796b690336310a0219c806ea343b
7
+ data.tar.gz: 71999dca3d06c3cedba61b8ffbe4a9c844884149269b20f0e45a3f9191cdb900f465f61f0b0f3cd3cf0bf67f16a7c071cb20da474a1be88db91df4cf2e09ac33
@@ -0,0 +1,7 @@
1
+ version: 2
2
+ updates:
3
+ - package-ecosystem: bundler
4
+ directory: "/"
5
+ schedule:
6
+ interval: daily
7
+ open-pull-requests-limit: 10
@@ -0,0 +1,43 @@
1
+ name: Ruby Gem
2
+
3
+ on:
4
+ push:
5
+ tags: [ 'v*.*.*' ]
6
+
7
+ jobs:
8
+ build:
9
+ name: Build + Publish
10
+ runs-on: ubuntu-latest
11
+ permissions:
12
+ contents: read
13
+ packages: write
14
+
15
+ steps:
16
+ - uses: actions/checkout@v2
17
+ - name: Set up Ruby 2.7
18
+ uses: actions/setup-ruby@v1
19
+ with:
20
+ ruby-version: 2.7.x
21
+
22
+ - name: Publish to GPR
23
+ run: |
24
+ mkdir -p $HOME/.gem
25
+ touch $HOME/.gem/credentials
26
+ chmod 0600 $HOME/.gem/credentials
27
+ printf -- "---\n:github: ${GEM_HOST_API_KEY}\n" > $HOME/.gem/credentials
28
+ gem build *.gemspec
29
+ gem push --KEY github --host https://rubygems.pkg.github.com/${OWNER} *.gem
30
+ env:
31
+ GEM_HOST_API_KEY: "Bearer ${{secrets.GITHUB_TOKEN}}"
32
+ OWNER: ${{ github.repository_owner }}
33
+
34
+ - name: Publish to RubyGems
35
+ run: |
36
+ mkdir -p $HOME/.gem
37
+ touch $HOME/.gem/credentials
38
+ chmod 0600 $HOME/.gem/credentials
39
+ printf -- "---\n:rubygems_api_key: ${GEM_HOST_API_KEY}\n" > $HOME/.gem/credentials
40
+ gem build *.gemspec
41
+ gem push *.gem
42
+ env:
43
+ GEM_HOST_API_KEY: "${{secrets.RUBYGEMS_AUTH_TOKEN}}"
@@ -0,0 +1,41 @@
1
+ # This workflow uses actions that are not certified by GitHub.
2
+ # They are provided by a third-party and are governed by
3
+ # separate terms of service, privacy policy, and support
4
+ # documentation.
5
+ # This workflow will download a prebuilt Ruby version, install dependencies and run tests with Rake
6
+ # For more information see: https://github.com/marketplace/actions/setup-ruby-jruby-and-truffleruby
7
+
8
+ name: Ruby
9
+
10
+ on:
11
+ push:
12
+ pull_request:
13
+ branches: [ main ]
14
+
15
+ jobs:
16
+ test:
17
+
18
+ runs-on: ubuntu-latest
19
+ strategy:
20
+ matrix:
21
+ ruby-version: ["2.7", "3.0", "3.1"]
22
+ allow-failure: [false]
23
+ include:
24
+ - ruby-version: "ruby-head"
25
+ allow-failure: true
26
+ continue-on-error: ${{ matrix.allow-failure }}
27
+
28
+ steps:
29
+ - name: Checkout 🛎
30
+ uses: actions/checkout@v2
31
+ - name: Set up Ruby
32
+ uses: ruby/setup-ruby@v1
33
+ with:
34
+ ruby-version: ${{ matrix.ruby-version }}
35
+ bundler-cache: true
36
+ - name: Run tests
37
+ run: bundle exec rake
38
+ - name: Run codacy-coverage-reporter 📈
39
+ uses: codacy/codacy-coverage-reporter-action@v1
40
+ with:
41
+ project-token: ${{ secrets.CODACY_PROJECT_TOKEN }}
data/.rubocop.yml CHANGED
@@ -1,5 +1,5 @@
1
1
  AllCops:
2
- TargetRubyVersion: 2.5
2
+ TargetRubyVersion: 2.7
3
3
  Metrics/AbcSize:
4
4
  Enabled: false
5
5
  Metrics/CyclomaticComplexity:
data/README.md CHANGED
@@ -1,14 +1,14 @@
1
1
  # Edits
2
2
 
3
- [![Gem Version](https://img.shields.io/gem/v/edits.svg)](https://rubygems.org/gems/edits)
4
- [![Build Status](https://travis-ci.org/tcrouch/edits.svg?branch=master)](https://travis-ci.org/tcrouch/edits)
5
- [![Codacy Badge](https://api.codacy.com/project/badge/Grade/64cb50b8e9ce4ec2a752d091e441b09d)](https://app.codacy.com/app/t.crouch/edits?utm_source=github.com&utm_medium=referral&utm_content=tcrouch/edits&utm_campaign=Badge_Grade_Dashboard)
6
- [![Inline docs](http://inch-ci.org/github/tcrouch/edits.svg?branch=master)](http://inch-ci.org/github/tcrouch/edits)
7
- [![Yard Docs](http://img.shields.io/badge/yard-docs-blue.svg)](http://rubydoc.info/github/tcrouch/edits)
3
+ [![Gem](https://img.shields.io/gem/v/edits)](https://rubygems.org/gems/edits)
4
+ [![GitHub Workflow Status (branch)](https://img.shields.io/github/workflow/status/tcrouch/edits/Ruby/main)](https://github.com/tcrouch/edits/actions/workflows/ruby.yml)
5
+ [![Inline docs](http://inch-ci.org/github/tcrouch/edits.svg?branch=master)](https://inch-ci.org/github/tcrouch/edits)
6
+ [![Yard Docs](https://img.shields.io/badge/yard-docs-informational)](https://rubydoc.info/github/tcrouch/edits)
8
7
 
9
8
  A collection of edit distance algorithms in Ruby.
10
9
 
11
- Includes Levenshtein, Restricted Edit (Optimal Alignment) and Damerau-Levenshtein distances, and Jaro & Jaro-Winkler similarity.
10
+ Includes Levenshtein, Restricted Edit (Optimal Alignment) and
11
+ Damerau-Levenshtein distances, and Jaro and Jaro-Winkler similarity.
12
12
 
13
13
  ## Installation
14
14
 
@@ -32,62 +32,57 @@ $ gem install edits
32
32
 
33
33
  ## Usage
34
34
 
35
- ### Levenshtein
35
+ ### Levenshtein variants
36
36
 
37
- Edit distance, accounting for deletion, addition and substitution.
37
+ Calculate the edit distance between two sequences with variants of the
38
+ Levenshtein distance algorithm.
38
39
 
39
40
  ```ruby
40
41
  Edits::Levenshtein.distance "raked", "bakers"
41
42
  # => 3
42
- Edits::Levenshtein.distance "iota", "atom"
43
- # => 4
44
- Edits::Levenshtein.distance "acer", "earn"
45
- # => 4
46
-
47
- # Max distance
48
- Edits::Levenshtein.distance_with_max "iota", "atom", 2
49
- # => 2
50
- Edits::Levenshtein.most_similar "atom", %w[tree rota toes racer]
51
- # => "toes"
43
+ Edits::RestrictedEdit.distance "iota", "atom"
44
+ # => 3
45
+ Edits::DamerauLevenshtein.distance "acer", "earn"
46
+ # => 3
52
47
  ```
53
48
 
54
- ### Restricted Edit (Optimal Alignment)
49
+ - **Levenshtein** edit distance, counting insertion, deletion and
50
+ substitution.
51
+ - **Restricted Damerau-Levenshtein** edit distance (aka **Optimal Alignment**),
52
+ counting insertion, deletion, substitution and transposition
53
+ (adjacent symbols swapped). Restricted by the condition that no substring is
54
+ edited more than once.
55
+ - **Damerau-Levenshtein** edit distance, counting insertion, deletion,
56
+ substitution and transposition (adjacent symbols swapped).
55
57
 
56
- Edit distance, accounting for deletion, addition, substitution and
57
- transposition (two adjacent characters are swapped). This variant is
58
- restricted by the condition that no sub-string is edited more than once.
58
+ | | Levenshtein | Restricted Damerau-Levenshtein | Damerau-Levenshtein |
59
+ |----------------------|-------------|--------------------------------|---------------------|
60
+ | "raked" vs. "bakers" | 3 | 3 | 3 |
61
+ | "iota" vs. "atom" | 4 | 3 | 3 |
62
+ | "acer" vs. "earn" | 4 | 4 | 3 |
59
63
 
60
- ```ruby
61
- Edits::RestrictedEdit.distance "raked", "bakers"
62
- # => 3
63
- Edits::RestrictedEdit.distance "iota", "atom"
64
- # => 3
65
- Edits::RestrictedEdit.distance "acer", "earn"
66
- # => 4
64
+ Levenshtein and Restricted Edit distances also have a bounded version.
67
65
 
66
+
67
+ ```ruby
68
68
  # Max distance
69
- Edits::RestrictedEdit.distance_with_max "iota", "atom", 2
70
- # => 2
71
- Edits::RestrictedEdit.most_similar "atom", %w[tree rota toes racer]
72
- # => "rota"
69
+ Edits::Levenshtein.distance_with_max "fghijk", "abcde", 3
70
+ # => 3
73
71
  ```
74
72
 
75
- ### Damerau-Levenshtein
76
-
77
- Edit distance, accounting for deletions, additions, substitution and
78
- transposition (two adjacent characters are swapped).
73
+ The convenience method `most_similar` searches for the best match to a
74
+ given sequence from a collection. It is similar to using `min_by`, but leverages
75
+ a maximum bound.
79
76
 
80
77
  ```ruby
81
- Edits::DamerauLevenshtein.distance "raked", "bakers"
82
- # => 3
83
- Edits::DamerauLevenshtein.distance "iota", "atom"
84
- # => 3
85
- Edits::DamerauLevenshtein.distance "acer", "earn"
86
- # => 3
78
+ Edits::RestrictedEdit.most_similar "atom", ["iota", "tome", "mown", "tame"]
79
+ # => "tome"
87
80
  ```
88
81
 
89
82
  ### Jaro & Jaro-Winkler
90
83
 
84
+ Calculate the Jaro and Jaro-Winkler similarity/distance of two sequences.
85
+
91
86
  ```ruby
92
87
  Edits::Jaro.similarity "information", "informant"
93
88
  # => 0.90235690235690236
@@ -102,6 +97,8 @@ Edits::JaroWinkler.distance "information", "informant"
102
97
 
103
98
  ### Hamming
104
99
 
100
+ Calculate the hamming distance between two sequences.
101
+
105
102
  ```ruby
106
103
  Edits::Hamming.distance("explorer", "exploded")
107
104
  # => 2
data/edits.gemspec CHANGED
@@ -25,13 +25,14 @@ Gem::Specification.new do |spec|
25
25
  spec.bindir = "exe"
26
26
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
27
27
  spec.require_paths = ["lib"]
28
+ spec.required_ruby_version = ">= 2.7"
28
29
 
29
- spec.add_development_dependency "benchmark-ips", "~> 2.7"
30
+ spec.add_development_dependency "benchmark-ips", "~> 2.8"
30
31
  spec.add_development_dependency "bundler", "~> 2.0"
31
32
  spec.add_development_dependency "rake", "~> 13.0"
32
- spec.add_development_dependency "redcarpet", "~> 3.4"
33
- spec.add_development_dependency "rspec", "~> 3.6"
34
- spec.add_development_dependency "simplecov", "~> 0.18"
33
+ spec.add_development_dependency "redcarpet", "~> 3.5"
34
+ spec.add_development_dependency "rspec", "~> 3.9"
35
+ spec.add_development_dependency "simplecov", "~> 0.21"
35
36
  spec.add_development_dependency "simplecov-lcov", "~> 0.8"
36
37
  spec.add_development_dependency "yard", "~> 0.9"
37
38
  end
data/lib/edits/compare.rb CHANGED
@@ -22,7 +22,7 @@ module Edits
22
22
  min_s = strings[0]
23
23
  min_d = distance(prototype, min_s)
24
24
 
25
- strings[1..-1].each do |s|
25
+ strings[1..].each do |s|
26
26
  return min_s if min_d.zero?
27
27
 
28
28
  d = distance_with_max(prototype, s, min_d)
@@ -21,70 +21,66 @@ module Edits
21
21
  seq1, seq2 = seq2, seq1 if seq1.length > seq2.length
22
22
 
23
23
  # array of codepoints outperforms String
24
- seq1 = seq1.codepoints if seq1.is_a? String
25
- seq2 = seq2.codepoints if seq2.is_a? String
24
+ if seq1.is_a?(String) && seq2.is_a?(String)
25
+ seq1 = seq1.codepoints
26
+ seq2 = seq2.codepoints
27
+ end
26
28
 
27
29
  rows = seq1.length
28
30
  cols = seq2.length
29
31
  return cols if rows == 0
30
32
  return rows if cols == 0
31
33
 
32
- # 'infinite' edit distance for padding cost matrix.
33
- # Can be any value > max[rows, cols]
34
- inf = rows + cols
35
-
36
- # Initialize first two rows of cost matrix.
37
- # The full initial state where cols=3, rows=2 (inf=5) would be:
38
- # [[5, 5, 5, 5, 5],
39
- # [5, 0, 1, 2, 3],
40
- # [5, 1, 0, 0, 0],
41
- # [5, 2, 0, 0, 0]]
42
- matrix = [Array.new(cols + 2, inf)]
43
- matrix << 0.upto(cols).to_a.unshift(inf)
34
+ # 'infinite' edit distance to pad cost matrix.
35
+ # Any value > max[rows, cols]
36
+ inf = cols + 1
44
37
 
45
38
  # element => last row seen
46
- item_history = Hash.new(0)
39
+ row_history = Hash.new(0)
47
40
 
48
- 1.upto(rows) do |row|
49
- # generate next row of cost matrix
50
- new_row = Array.new(cols + 2, 0)
51
- new_row[0] = inf
52
- new_row[1] = row
53
- matrix << new_row
41
+ # initialize alphabet-keyed cost matrix
42
+ matrix = {}
43
+ curr_row = 0.upto(cols).to_a
54
44
 
55
- last_match_col = 0
56
- seq1_item = seq1[row - 1]
45
+ rows.times do |row|
46
+ seq1_item = seq1[row]
47
+ match_col = 0
57
48
 
58
- 1.upto(cols) do |col|
59
- seq2_item = seq2[col - 1]
60
- last_match_row = item_history[seq2_item]
49
+ # rotate row arrays & generate next
50
+ matrix[seq1_item] = last_row = curr_row
51
+ curr_row = Array.new(cols + 1, inf)
52
+ curr_row[0] = row + 1
61
53
 
54
+ cols.times do |col|
55
+ seq2_item = seq2[col]
62
56
  sub_cost = seq1_item == seq2_item ? 0 : 1
63
57
 
64
- transposition = 1 + matrix[last_match_row][last_match_col]
65
- transposition += row - last_match_row - 1
66
- transposition += col - last_match_col - 1
67
-
68
- # TODO: do insertion/deletion need to be considered when
69
- # seq1_item == seq2_item ?
70
- #
71
- # substitution, deletion, insertion, transposition
58
+ # | Xs | Xd |
59
+ # | Xi | ? |
60
+ # substitution, deletion, insertion
72
61
  cost = [
73
- matrix[row][col] + sub_cost,
74
- matrix[row][col + 1] + 1,
75
- matrix[row + 1][col] + 1,
76
- transposition
62
+ last_row[col] + sub_cost,
63
+ last_row[col + 1] + 1,
64
+ curr_row[col] + 1
77
65
  ].min
78
66
 
79
- matrix[row + 1][col + 1] = cost
80
-
81
- last_match_col = col if sub_cost == 0
67
+ # transposition cost
68
+ # skip missed matrix lookup (inf cost)
69
+ if sub_cost > 0 && row > 0 && (m = matrix[seq2_item])
70
+ transpose = 1 + m[match_col] \
71
+ + (row - row_history[seq2_item] - 1) \
72
+ + (col - match_col - 1)
73
+ cost = transpose if transpose < cost
74
+ end
75
+
76
+ match_col = col if sub_cost == 0
77
+ curr_row[col + 1] = cost
82
78
  end
83
79
 
84
- item_history[seq1_item] = row
80
+ row_history[seq1_item] = row
85
81
  end
86
82
 
87
- matrix[rows + 1][cols + 1]
83
+ curr_row[cols]
88
84
  end
89
85
  end
90
86
  end
data/lib/edits/version.rb CHANGED
@@ -2,5 +2,5 @@
2
2
 
3
3
  module Edits
4
4
  # Current gem version
5
- VERSION = "0.3.0"
5
+ VERSION = "0.5.0"
6
6
  end
@@ -162,7 +162,7 @@ namespace :benchmark do
162
162
 
163
163
  x.report "literal, +" do
164
164
  m = []
165
- m << [rows] + Array.new(cols, 0)
165
+ m << ([rows] + Array.new(cols, 0))
166
166
  end
167
167
 
168
168
  x.compare!
@@ -195,7 +195,7 @@ namespace :benchmark do
195
195
 
196
196
  x.report "literal, +" do
197
197
  m = []
198
- m << [rows, inf] + Array.new(cols, 0)
198
+ m << ([rows, inf] + Array.new(cols, 0))
199
199
  end
200
200
 
201
201
  x.compare!
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: edits
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tom Crouch
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-05-21 00:00:00.000000000 Z
11
+ date: 2022-10-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: benchmark-ips
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '2.7'
19
+ version: '2.8'
20
20
  type: :development
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: '2.7'
26
+ version: '2.8'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: bundler
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -58,42 +58,42 @@ dependencies:
58
58
  requirements:
59
59
  - - "~>"
60
60
  - !ruby/object:Gem::Version
61
- version: '3.4'
61
+ version: '3.5'
62
62
  type: :development
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
66
  - - "~>"
67
67
  - !ruby/object:Gem::Version
68
- version: '3.4'
68
+ version: '3.5'
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: rspec
71
71
  requirement: !ruby/object:Gem::Requirement
72
72
  requirements:
73
73
  - - "~>"
74
74
  - !ruby/object:Gem::Version
75
- version: '3.6'
75
+ version: '3.9'
76
76
  type: :development
77
77
  prerelease: false
78
78
  version_requirements: !ruby/object:Gem::Requirement
79
79
  requirements:
80
80
  - - "~>"
81
81
  - !ruby/object:Gem::Version
82
- version: '3.6'
82
+ version: '3.9'
83
83
  - !ruby/object:Gem::Dependency
84
84
  name: simplecov
85
85
  requirement: !ruby/object:Gem::Requirement
86
86
  requirements:
87
87
  - - "~>"
88
88
  - !ruby/object:Gem::Version
89
- version: '0.18'
89
+ version: '0.21'
90
90
  type: :development
91
91
  prerelease: false
92
92
  version_requirements: !ruby/object:Gem::Requirement
93
93
  requirements:
94
94
  - - "~>"
95
95
  - !ruby/object:Gem::Version
96
- version: '0.18'
96
+ version: '0.21'
97
97
  - !ruby/object:Gem::Dependency
98
98
  name: simplecov-lcov
99
99
  requirement: !ruby/object:Gem::Requirement
@@ -132,10 +132,12 @@ executables: []
132
132
  extensions: []
133
133
  extra_rdoc_files: []
134
134
  files:
135
+ - ".github/dependabot.yml"
136
+ - ".github/workflows/gem-push.yml"
137
+ - ".github/workflows/ruby.yml"
135
138
  - ".gitignore"
136
139
  - ".rspec"
137
140
  - ".rubocop.yml"
138
- - ".travis.yml"
139
141
  - ".yardopts"
140
142
  - Gemfile
141
143
  - LICENSE.txt
@@ -167,15 +169,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
167
169
  requirements:
168
170
  - - ">="
169
171
  - !ruby/object:Gem::Version
170
- version: '0'
172
+ version: '2.7'
171
173
  required_rubygems_version: !ruby/object:Gem::Requirement
172
174
  requirements:
173
175
  - - ">="
174
176
  - !ruby/object:Gem::Version
175
177
  version: '0'
176
178
  requirements: []
177
- rubyforge_project:
178
- rubygems_version: 2.7.7
179
+ rubygems_version: 3.1.6
179
180
  signing_key:
180
181
  specification_version: 4
181
182
  summary: A collection of edit distance algorithms.
data/.travis.yml DELETED
@@ -1,35 +0,0 @@
1
- os: linux
2
- language: ruby
3
- cache: bundler
4
- rvm:
5
- - 2.4
6
- - 2.5
7
- - 2.6.5
8
- - 2.7
9
- - ruby-head
10
- - rbx-3
11
- before_install:
12
- - gem update --system
13
- - gem install bundler
14
- - sudo apt-get install jq
15
- - curl -LSs "$(curl -LSs https://api.github.com/repos/codacy/codacy-coverage-reporter/releases/latest | jq -r '.assets | map({name, browser_download_url} | select(.name | endswith(".jar"))) | .[0].browser_download_url')" -o codacy-coverage-reporter-assembly.jar
16
- after_success:
17
- - java -jar codacy-coverage-reporter-assembly.jar report -l Ruby -r coverage/lcov/edits.lcov
18
- jobs:
19
- allow_failures:
20
- - rvm: ruby-head
21
- - rvm: rbx-3
22
- fast_finish: true
23
- include:
24
- - stage: gem release
25
- if: tag IS present
26
- rvm: 2.6.5
27
- script: echo "Deploying to rubygems.org ..."
28
- deploy:
29
- provider: rubygems
30
- api_key:
31
- secure: NKUWS12yMpsMtPeKL+4X56xZ02p5y4R+HYQmgvxFNajOS42aTMXncx4eTnuHoNdKbh+x5yci+lbPr+j5t9qWo5GzJsxjqnFW4lO2V5O1ONBzcAB/g6BPDMQsWbX34eBVafCEidNoOY1HhLlKerWzlsGRq+P4q+3WKLJihrl99pyv+EQykqS11/YVCsmFjrPVa/aGk84njbnI/kIDOY5HV2gVBazARixRO5y2AUg3hRUf2+Tu/X1ke80YBD9LazW2kRUzz0Rs+1vc653JtYn6MeK/bEkGWvN2Qs/k6Q2nNr6ni4v39Y07yylu1EnYh1H/0OT/H2hehxsjMQCQhDlVKbN7NTmOV026aWXA2HdSxmVhxQCIKRGW3Nm81kBj1/edXLpJActnLeex2iCMcXJk8yAAzF0q+vSHLld0w9Jx95kIJB4tnaonWJxcaWaX58HWFbdOuYKvQhXqcflI4KmNH8xXm/O0FIM8VEJRg9dojZ5S8Us3fZpBFZxVJ3H3Fcb406AmoIqcHOsqJ1GvBM8EdWkwuaH9GsUWf8pydgKFgStYUaKk8DDmJonT748emG4yw+78uMGWPoFxf+Mc8jazRxIaRiQbjVFUcLjaRkRFlL8UT9BB9k7c1egjvKhUo/pqV7KIEsAJrrh6zZIkz4h9AwxWpZjVj9+z63Kh6NC1lUI=
32
- gem: edits
33
- on:
34
- tags: true
35
- repo: tcrouch/edits