license_matcher 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 75f4cc4338e87a4244875d5172770867b63588cb
4
- data.tar.gz: 3bb514e6e029140949ed3c78c1b32a1a04333a04
3
+ metadata.gz: bae726cf67cb9e615291d1da18e87f120fdb7ea6
4
+ data.tar.gz: d104b7310dc786821c56df1b3d984e9069dc1434
5
5
  SHA512:
6
- metadata.gz: 1b99befeaf51c94e2f85acdc991abd02486de41ea3f7506a2e7a6402f08e4c0765b322cb480db98703da01ef286bf9751bc6fc458356e1c93c4142f611a26457
7
- data.tar.gz: 67b3d8e0265f91ebd9d1818983dfea906f543dffcc08d41d3cb50aeb66d56fe76714409085e741513a36229cbaefef388e0ffe691e20e06ec558796eef6f4ac0
6
+ metadata.gz: 72c13e2a98ce021d8491063244f00c9ef56ec3ef7332d77448485d93d92cdcead16e20cb5dc0b01726970defed06dafa4bc2ac121a867df1393dace608a3e951
7
+ data.tar.gz: d11e26305040d796f072294f6bdb9c20e09e3fcaf25e2d519e0737bf1647a4ac65166795a030a35f4938b8fffbbf2cb66ae00f64edc9066b124f49aa93c090ed
data/Gemfile.lock CHANGED
@@ -1,11 +1,13 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- license_matcher (0.2.0.pre.alpha)
5
- helix_runtime (~> 0.6.0)
4
+ license_matcher (0.3.0)
5
+ helix_runtime (~> 0.6)
6
+ msgpack (~> 1.1)
6
7
  narray (~> 0.6.1.2)
7
- nokogiri (~> 1.8.0)
8
- tf-idf-similarity (~> 0.1.6)
8
+ nokogiri (~> 1.8)
9
+ os (~> 1.0)
10
+ tf-idf-similarity (~> 0.1)
9
11
 
10
12
  GEM
11
13
  remote: https://rubygems.org/
@@ -16,11 +18,12 @@ GEM
16
18
  rake (>= 10.0)
17
19
  thor (~> 0.19.4)
18
20
  toml (~> 0.1.2)
19
- mini_portile2 (2.2.0)
21
+ mini_portile2 (2.3.0)
20
22
  msgpack (1.1.0)
21
23
  narray (0.6.1.2)
22
- nokogiri (1.8.0)
23
- mini_portile2 (~> 2.2.0)
24
+ nokogiri (1.8.1)
25
+ mini_portile2 (~> 2.3.0)
26
+ os (1.0.0)
24
27
  parslet (1.5.0)
25
28
  blankslate (~> 2.0)
26
29
  rake (10.5.0)
@@ -50,7 +53,6 @@ PLATFORMS
50
53
  DEPENDENCIES
51
54
  bundler (~> 1.15)
52
55
  license_matcher!
53
- msgpack (~> 1.1.0)
54
56
  rake (~> 10.0)
55
57
  rspec (~> 3.4)
56
58
 
data/README.md CHANGED
@@ -44,7 +44,7 @@ LicenseMatcher::IndexBuilder.build_index( "data/licenses", "data/index.msgpack")
44
44
  txt = File.read("fixtures/files/mit.txt");
45
45
 
46
46
  lm = LicenseMatcher::TFRubyMatcher.new("data/index.msgpack")
47
- m = lm.match_text(txt, 0.9)
47
+ m = lm.match_text(txt, 0.9)
48
48
  p "spdx id: #{m.get_label()}, confidence: #{m.get_score()}"
49
49
 
50
50
  ```
@@ -78,7 +78,7 @@ lm.match_rules "It is license under Apache 2.0 License."
78
78
  lm = LicenseMatcher::TFRubyMatcher.new
79
79
 
80
80
  txt = File.read "fixtures/files/mit.html"
81
- clean_txt = lm.preprocess_html txt # NB! it may help to increase accuracy
81
+ clean_txt = LicenseMatcher::Preprocess.preprocess_html txt # NB! it may help to increase accuracy
82
82
  lm.match_txt clean_txt
83
83
  ```
84
84
 
@@ -95,7 +95,7 @@ lm2.match_text txt
95
95
 
96
96
  ```
97
97
  lm3 = File.read "fixtures/files/mit.txt"
98
- lm3.match_text txt
98
+ lm3.match_text txt
99
99
  ```
100
100
 
101
101
  ## Benchmarks
@@ -138,7 +138,7 @@ To install this gem onto your local machine, run `bundle exec rake install`. To
138
138
 
139
139
  ## Contributing
140
140
 
141
- Bug reports and pull requests are welcome on GitHub at https://github.com/fosslim/license_matcher.
141
+ Bug reports and pull requests are welcome on GitHub at https://github.com/fosslim/license_matcher.
142
142
 
143
143
  This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
144
144
 
Binary file
@@ -1,75 +1,77 @@
1
1
  require 'nokogiri'
2
2
 
3
- module Preprocess
4
- def preprocess_text(text)
5
- text = safe_encode(text)
3
+ module LicenseMatcher
4
+ class Preprocess
5
+ def self.preprocess_text(text)
6
+ text = safe_encode(text)
6
7
 
7
- #remove markdown url tags
8
- text = text.gsub(/\[.+?\]\(.+?\)/, ' ')
8
+ #remove markdown url tags
9
+ text = text.gsub(/\[.+?\]\(.+?\)/, ' ')
9
10
 
10
- #remove spam words
11
- text.gsub!(/\bTHE\b/i, '')
11
+ #remove spam words
12
+ text.gsub!(/\bTHE\b/i, '')
12
13
 
13
- #remove some XML grabage
14
- text = text.gsub(/\<\!\[CDATA.*?\]\]\>/, ' ').to_s
15
- text = text.gsub(/\<\!--.+?--\>/, ' ').to_s
16
- text = text.gsub(/<\!\[CDATA.+?\]>/, ' ').to_s
14
+ #remove some XML grabage
15
+ text = text.gsub(/\<\!\[CDATA.*?\]\]\>/, ' ').to_s
16
+ text = text.gsub(/\<\!--.+?--\>/, ' ').to_s
17
+ text = text.gsub(/<\!\[CDATA.+?\]>/, ' ').to_s
17
18
 
18
- return text.to_s.strip.gsub(/\s+/, ' ')
19
- end
19
+ return text.to_s.strip.gsub(/\s+/, ' ')
20
+ end
20
21
 
21
- def preprocess_html(html_text)
22
- # if text is HTML doc, then
23
- # extract text only from visible html tags
24
- text = ""
22
+ def self.preprocess_html(html_text)
23
+ # if text is HTML doc, then
24
+ # extract text only from visible html tags
25
+ text = ""
25
26
 
26
- html_doc = parse_html(html_text)
27
- if html_doc
28
- text = clean_html(html_doc)
29
- else
30
- p "match_html: failed to parse html document\n#{html_text}"
27
+ html_doc = parse_html(html_text)
28
+ if html_doc
29
+ text = clean_html(html_doc)
30
+ else
31
+ p "match_html: failed to parse html document\n#{html_text}"
32
+ end
33
+
34
+ return text
31
35
  end
32
36
 
33
- return text
34
- end
37
+ def self.clean_html(html_doc)
38
+ body_text = ""
39
+ body_elements = html_doc.xpath(
40
+ '//p | //h1 | //h2 | //h3 | //h4 | //h5 | //h6 | //em | //strong | //b | //td | //pre
41
+ | //li[not(@id) and not(@class) and not(a)] | //section//section[@class="project-info"]
42
+ | //blockquote | //textarea'
43
+ ).to_a
35
44
 
36
- def clean_html(html_doc)
37
- body_text = ""
38
- body_elements = html_doc.xpath(
39
- '//p | //h1 | //h2 | //h3 | //h4 | //h5 | //h6 | //em | //strong | //b | //td | //pre
40
- | //li[not(@id) and not(@class) and not(a)] | //section//section[@class="project-info"]
41
- | //blockquote | //textarea'
42
- ).to_a
45
+ #extract text from html tag and separate them by space
46
+ body_elements.each {|el| body_text += ' ' + el.text.to_s}
43
47
 
44
- #extract text from html tag and separate them by space
45
- body_elements.each {|el| body_text += ' ' + el.text.to_s}
48
+ #REMOVE XML CDATA like opensource.org pages has
49
+ body_text = body_text.to_s.strip
50
+ body_text.gsub!(/\<\!\[CDATA.+?\]\]\>/i, ' ')
46
51
 
47
- #REMOVE XML CDATA like opensource.org pages has
48
- body_text = body_text.to_s.strip
49
- body_text.gsub!(/\<\!\[CDATA.+?\]\]\>/i, ' ')
52
+ if body_text.empty?
53
+ p "match_html: document didnt pass noise filter, will use whole body content"
54
+ body_text = html_doc.xpath('//body').text.to_s.strip
55
+ end
50
56
 
51
- if body_text.empty?
52
- p "match_html: document didnt pass noise filter, will use whole body content"
53
- body_text = html_doc.xpath('//body').text.to_s.strip
57
+ return body_text
54
58
  end
55
59
 
56
- return body_text
57
- end
60
+ def self.parse_html(html_text)
61
+ begin
62
+ return Nokogiri.HTML(safe_encode(html_text))
63
+ rescue Exception => e
64
+ p "failed to parse html doc: \n #{html_text} - #{e.message}"
65
+ return nil
66
+ end
67
+ end
58
68
 
59
- def parse_html(html_text)
60
- begin
61
- return Nokogiri.HTML(safe_encode(html_text))
62
- rescue Exception => e
63
- log.error "failed to parse html doc: \n #{html_text}"
64
- return nil
69
+ def self.safe_encode(txt)
70
+ txt.to_s.encode('UTF-8', 'binary', invalid: :replace, undef: :replace, replace: '')
71
+ rescue
72
+ p "Failed to encode text:\n #{txt}i"
73
+ return ""
65
74
  end
66
- end
67
75
 
68
- def safe_encode(txt)
69
- txt.to_s.encode('UTF-8', 'binary', invalid: :replace, undef: :replace, replace: '')
70
- rescue
71
- p "Failed to encode text:\n #{txt}i"
72
- return ""
73
76
  end
74
-
75
- end
77
+ end
@@ -1,14 +1,11 @@
1
1
  require 'json'
2
2
 
3
3
  module LicenseMatcher
4
-
5
- class RuleMatcher
6
- include Preprocess
7
4
 
5
+ class RuleMatcher
8
6
  attr_reader :licenses, :rules, :id_spdx_idx
9
7
 
10
- DEFAULT_LICENSE_JSON = 'data/spdx_licenses/licenses.json'
11
-
8
+ DEFAULT_LICENSE_JSON = 'data/licenses.json'
12
9
 
13
10
  def initialize(license_json_file = DEFAULT_LICENSE_JSON)
14
11
 
@@ -31,6 +28,16 @@ module LicenseMatcher
31
28
  idx
32
29
  end
33
30
 
31
+ def match_text(text, min_confidence = 0.0)
32
+ res = match_rules(text, true)
33
+ if res.empty?
34
+ Match.new("", 0.0)
35
+ else
36
+ spdx_id, score = res.first
37
+ Match.new(spdx_id, score.to_f)
38
+ end
39
+ end
40
+
34
41
  # finds matching regex rules in the text and sorts matches by length of match
35
42
  # ps: not very efficient, but good enough to handle special cases;
36
43
  # @args:
@@ -39,7 +46,7 @@ module LicenseMatcher
39
46
  # [[spdx_id, score, matching_rule, matching_length],...]
40
47
  def match_rules(text, early_exit = false)
41
48
  matches = []
42
- text = preprocess_text(text)
49
+ text = LicenseMatcher::Preprocess.preprocess_text(text)
43
50
 
44
51
  #if text is already spdx_id, then shortcut matching
45
52
  if @rules.has_key?(text.downcase)
@@ -90,7 +97,6 @@ module LicenseMatcher
90
97
  def read_json_file(file_path)
91
98
  JSON.parse(File.read(file_path), {symbolize_names: true})
92
99
  rescue
93
- log.info "Failed to read json file `#{file_path}`"
94
100
  nil
95
101
  end
96
102
 
@@ -144,7 +150,7 @@ module LicenseMatcher
144
150
  end
145
151
 
146
152
 
147
- spdx_name = preprocess_text(spdx_item[:name])
153
+ spdx_name = LicenseMatcher::Preprocess.preprocess_text(spdx_item[:name])
148
154
  spdx_name.gsub!(/\(.+?\)/, '') #remove SPDX ids in the license names
149
155
  spdx_name.gsub!(/\./, '\\.') #mark version dots as not regex selector
150
156
  spdx_name.gsub!(/[\*|\?|\+]/, '.') #replace regex selector with whatever mark ~> WTFPL name
@@ -598,4 +604,4 @@ module LicenseMatcher
598
604
  end
599
605
 
600
606
  end
601
- end
607
+ end
@@ -5,7 +5,6 @@ require 'msgpack'
5
5
  module LicenseMatcher
6
6
 
7
7
  class TFRubyMatcher
8
- include Preprocess
9
8
 
10
9
  attr_reader :corpus, :model, :spdx_ids
11
10
 
@@ -26,10 +25,9 @@ module LicenseMatcher
26
25
  # matches given text with SPDX licenses and returns Match object
27
26
  # returns:
28
27
  # match - Match {label: String, score: float}
29
- def match_text(text, min_confidence = DEFAULT_MIN_CONFIDENCE, is_processed_text = false)
28
+ def match_text(text, min_confidence = DEFAULT_MIN_CONFIDENCE)
30
29
  return [] if text.to_s.empty?
31
30
 
32
- text = preprocess_text(text) if is_processed_text == false
33
31
  test_doc = TfIdfSimilarity::Document.new(text, {:id => "test"})
34
32
 
35
33
  mat1 = @model.instance_variable_get(:@matrix)
@@ -52,7 +50,8 @@ module LicenseMatcher
52
50
  end
53
51
 
54
52
  def match_html(html_text, min_confidence = DEFAULT_MIN_CONFIDENCE)
55
- match_text(preprocess_html(html_text), min_confidence)
53
+ clean_text = LicenseMatcher::Preprocess.preprocess_html(html_text)
54
+ match_text(clean_text, min_confidence)
56
55
  end
57
56
 
58
57
  #-- helpers
@@ -89,7 +88,7 @@ module LicenseMatcher
89
88
 
90
89
  idx[A_DOC_ROW].to_a.each do |doc_row|
91
90
  _, spdx_id, content, _ = doc_row
92
- txt = preprocess_text content
91
+ txt = LicenseMatcher::Preprocess.preprocess_text content
93
92
  if txt
94
93
  spdx_ids << spdx_id
95
94
  docs << TfIdfSimilarity::Document.new(txt, :id => spdx_id)
@@ -1,10 +1,9 @@
1
1
 
2
2
  module LicenseMatcher
3
-
4
3
  class UrlMatcher
5
4
  attr_reader :url_index
6
5
 
7
- DEFAULT_LICENSE_JSON = 'data/spdx_licenses/licenses.json'
6
+ DEFAULT_LICENSE_JSON = 'data/licenses.json'
8
7
 
9
8
  def initialize(license_json_file = DEFAULT_LICENSE_JSON)
10
9
  licenses_json_doc = read_json_file license_json_file
@@ -13,33 +12,42 @@ module LicenseMatcher
13
12
  @url_index = read_license_url_index(licenses_json_doc)
14
13
  end
15
14
 
16
- # Matches License.url with urls in Licenses.json and returns tuple [spdx_id, score]
15
+ def match_text(url_txt, min_confidence = 0.0)
16
+ spdx_id, score = match_url
17
+ if spdx_idx
18
+ Match.new(spdx_id, score.to_f)
19
+ else
20
+ Match.new("", 0.0)
21
+ end
22
+ end
23
+
24
+ # Matches License.url with urls in Licenses.json and returns tuple [spdx_id, score]
17
25
  def match_url(the_url)
18
26
  the_url = the_url.to_s.strip
19
27
  spdx_id = nil
20
28
 
21
29
  case the_url
22
- when 'http://jquery.org/license'
30
+ when /jquery\.org\/license/i
23
31
  return ['mit', 1.0] #Jquery license page doesnt include any license text
24
- when 'https://www.mozilla.org/en-US/MPL/'
32
+ when /mozilla\.org\/en-US\/MPL/i
25
33
  return ['mpl-2.0', 1.0]
26
- when 'http://fairlicense.org'
34
+ when /fairlicense\.org/i
27
35
  return ['fair', 1.0]
28
- when 'http://www.aforgenet.com/framework/license.html'
36
+ when /aforgenet\.com\/framework\/license/i
29
37
  return ['lgpl-3.0', 1.0]
30
- when 'http://www.apache.org/licenses/'
38
+ when /apache\.org\/licenses/i
31
39
  return ['apache-2.0', 1.0]
32
- when 'http://aws.amazon.com/apache2.0/'
40
+ when /aws\.amazon\.com\/apache2\.0/i
33
41
  return ['apache-2.0', 1.0]
34
- when 'http://aws.amazon.com/asl/'
42
+ when /aws\.amazon\.com\/asl/i
35
43
  return ['amazon', 1.0]
36
- when 'https://choosealicense.com/no-license/'
44
+ when /choosealicense\.com\/no-license/i
37
45
  return ['no-license', 1.0]
38
- when 'http://www.gzip.org/zlib/zlib_license.html'
46
+ when /gzip\.org\/zlib\/zlib?license/i
39
47
  return ['zlib', 1.0]
40
- when 'http://zlib.net/zlib-license.html'
48
+ when /zlib\.net\/zlib?license/i
41
49
  return ['zlib', 1.0]
42
- when 'http://www.wtfpl.net/about/'
50
+ when /wtfpl\.net\/about/i
43
51
  return ['wtfpl', 1.0]
44
52
  end
45
53
 
@@ -1,9 +1,21 @@
1
+ require 'os'
1
2
  require "helix_runtime"
2
3
 
3
- begin
4
+
5
+ if OS.mac?
6
+ begin
4
7
  require "license_matcher/native"
5
- rescue LoadError
6
- warn "Unable to load license_matcher/native. Please run `rake build`"
8
+ rescue LoadError
9
+ warn "Failed to load native extensions for OSx. Please run `rake build`"
10
+ end
11
+ elsif OS.posix?
12
+ begin
13
+ require "license_matcher/native.so"
14
+ rescue LoadError
15
+ warn "Failed to load native extensions for Posix. Please run `rake build`"
16
+ end
17
+ else
18
+ warn "Unsupported platform - you are not able to use some models;"
7
19
  end
8
20
 
9
21
  require 'license_matcher/preprocess'
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: license_matcher
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Timo Sulg
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2017-09-27 00:00:00.000000000 Z
12
+ date: 2017-09-30 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: helix_runtime
@@ -17,14 +17,14 @@ dependencies:
17
17
  requirements:
18
18
  - - "~>"
19
19
  - !ruby/object:Gem::Version
20
- version: 0.6.0
20
+ version: '0.6'
21
21
  type: :runtime
22
22
  prerelease: false
23
23
  version_requirements: !ruby/object:Gem::Requirement
24
24
  requirements:
25
25
  - - "~>"
26
26
  - !ruby/object:Gem::Version
27
- version: 0.6.0
27
+ version: '0.6'
28
28
  - !ruby/object:Gem::Dependency
29
29
  name: narray
30
30
  requirement: !ruby/object:Gem::Requirement
@@ -45,84 +45,98 @@ dependencies:
45
45
  requirements:
46
46
  - - "~>"
47
47
  - !ruby/object:Gem::Version
48
- version: 0.1.6
48
+ version: '0.1'
49
49
  type: :runtime
50
50
  prerelease: false
51
51
  version_requirements: !ruby/object:Gem::Requirement
52
52
  requirements:
53
53
  - - "~>"
54
54
  - !ruby/object:Gem::Version
55
- version: 0.1.6
55
+ version: '0.1'
56
56
  - !ruby/object:Gem::Dependency
57
57
  name: nokogiri
58
58
  requirement: !ruby/object:Gem::Requirement
59
59
  requirements:
60
60
  - - "~>"
61
61
  - !ruby/object:Gem::Version
62
- version: 1.8.0
62
+ version: '1.8'
63
63
  type: :runtime
64
64
  prerelease: false
65
65
  version_requirements: !ruby/object:Gem::Requirement
66
66
  requirements:
67
67
  - - "~>"
68
68
  - !ruby/object:Gem::Version
69
- version: 1.8.0
69
+ version: '1.8'
70
70
  - !ruby/object:Gem::Dependency
71
- name: bundler
71
+ name: msgpack
72
72
  requirement: !ruby/object:Gem::Requirement
73
73
  requirements:
74
74
  - - "~>"
75
75
  - !ruby/object:Gem::Version
76
- version: '1.15'
77
- type: :development
76
+ version: '1.1'
77
+ type: :runtime
78
78
  prerelease: false
79
79
  version_requirements: !ruby/object:Gem::Requirement
80
80
  requirements:
81
81
  - - "~>"
82
82
  - !ruby/object:Gem::Version
83
- version: '1.15'
83
+ version: '1.1'
84
84
  - !ruby/object:Gem::Dependency
85
- name: rake
85
+ name: os
86
86
  requirement: !ruby/object:Gem::Requirement
87
87
  requirements:
88
88
  - - "~>"
89
89
  - !ruby/object:Gem::Version
90
- version: '10.0'
90
+ version: '1.0'
91
+ type: :runtime
92
+ prerelease: false
93
+ version_requirements: !ruby/object:Gem::Requirement
94
+ requirements:
95
+ - - "~>"
96
+ - !ruby/object:Gem::Version
97
+ version: '1.0'
98
+ - !ruby/object:Gem::Dependency
99
+ name: bundler
100
+ requirement: !ruby/object:Gem::Requirement
101
+ requirements:
102
+ - - "~>"
103
+ - !ruby/object:Gem::Version
104
+ version: '1.15'
91
105
  type: :development
92
106
  prerelease: false
93
107
  version_requirements: !ruby/object:Gem::Requirement
94
108
  requirements:
95
109
  - - "~>"
96
110
  - !ruby/object:Gem::Version
97
- version: '10.0'
111
+ version: '1.15'
98
112
  - !ruby/object:Gem::Dependency
99
- name: rspec
113
+ name: rake
100
114
  requirement: !ruby/object:Gem::Requirement
101
115
  requirements:
102
116
  - - "~>"
103
117
  - !ruby/object:Gem::Version
104
- version: '3.4'
118
+ version: '10.0'
105
119
  type: :development
106
120
  prerelease: false
107
121
  version_requirements: !ruby/object:Gem::Requirement
108
122
  requirements:
109
123
  - - "~>"
110
124
  - !ruby/object:Gem::Version
111
- version: '3.4'
125
+ version: '10.0'
112
126
  - !ruby/object:Gem::Dependency
113
- name: msgpack
127
+ name: rspec
114
128
  requirement: !ruby/object:Gem::Requirement
115
129
  requirements:
116
130
  - - "~>"
117
131
  - !ruby/object:Gem::Version
118
- version: 1.1.0
132
+ version: '3.4'
119
133
  type: :development
120
134
  prerelease: false
121
135
  version_requirements: !ruby/object:Gem::Requirement
122
136
  requirements:
123
137
  - - "~>"
124
138
  - !ruby/object:Gem::Version
125
- version: 1.1.0
139
+ version: '3.4'
126
140
  description: "\n LicenseMatcher is rubygem, which uses Fosslim to match various
127
141
  OSS license\n with correct SPDX-id or EULA label.\n "
128
142
  email:
@@ -142,6 +156,7 @@ files:
142
156
  - Rakefile
143
157
  - lib/license_matcher.rb
144
158
  - lib/license_matcher/native.bundle
159
+ - lib/license_matcher/native.so
145
160
  - lib/license_matcher/preprocess.rb
146
161
  - lib/license_matcher/rule_matcher.rb
147
162
  - lib/license_matcher/tf_ruby_matcher.rb