licensee 4.9.0 → 5.0.0b1

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
  SHA1:
3
- metadata.gz: e32073ffb1db6eed03c14cee02c4d72096fc70c8
4
- data.tar.gz: f658b84856962074cebd0a310510510b7aa34dac
3
+ metadata.gz: 49baf7865bddf84e8237149cba6f21768e661c39
4
+ data.tar.gz: 95dbc39d96bed4d0952df9e85eb56ae37f387c1f
5
5
  SHA512:
6
- metadata.gz: 36b583c9dfc992f30e54425046d79f23b4522b31e470b54d61e1d08e20b146be096fdf8a684e0b45780ff487108a9bf5850452ba002883a1e8d357d316ef87c3
7
- data.tar.gz: 682553fa645a933c8bf3c2db07a29a59ff5269564aef2c58604bf17351212508e8f74f9e51388d71894f4e844c6bf52bc21dca1c6385f598695e67d4fb9cdb39
6
+ metadata.gz: 60a4c027e6d388d732ad3588606c380d90595be0955f55e9415a727370c78c8faff40c781843bc04989fb3a591821f91579079f54c2197f02778e9cac6cb73a3
7
+ data.tar.gz: 510467b863f02cdb658986dd5626460d0383c5602d22c3f170094d7d38bf80e56727691a4c8fc30dc7b4c79003a610383a90dfa4864fc87b1f3a4f83eb404012
data/bin/licensee CHANGED
@@ -3,11 +3,12 @@ require_relative "../lib/licensee"
3
3
 
4
4
  path = ARGV[0] || Dir.pwd
5
5
 
6
+ Licensee.package_manager_files = true
6
7
  project = Licensee::Project.new(path)
7
- license = project.license_file
8
+ license = project.matched_file
8
9
 
9
10
  if license
10
- puts "License file: #{license.path}"
11
+ puts "Matched file: #{license.path}"
11
12
  puts "License: #{license.match ? license.match.meta['title'] : 'no license'}"
12
13
  puts "Confidence: #{license.confidence}%"
13
14
  puts "Method: #{license.matcher.class}"
data/lib/licensee.rb CHANGED
@@ -5,15 +5,17 @@ require 'levenshtein'
5
5
 
6
6
  require_relative "licensee/version"
7
7
  require_relative "licensee/license"
8
- require_relative "licensee/licenses"
9
- require_relative "licensee/license_file"
10
8
  require_relative "licensee/project"
9
+ require_relative "licensee/project_file"
11
10
  require_relative "licensee/filesystem_repository"
12
11
  require_relative "licensee/matcher"
13
12
  require_relative "licensee/matchers/exact_matcher"
14
13
  require_relative "licensee/matchers/copyright_matcher"
15
14
  require_relative "licensee/matchers/git_matcher"
16
15
  require_relative "licensee/matchers/levenshtein_matcher"
16
+ require_relative "licensee/matchers/package_matcher"
17
+ require_relative "licensee/matchers/gemspec_matcher"
18
+ require_relative "licensee/matchers/npm_bower_matcher"
17
19
 
18
20
  class Licensee
19
21
 
@@ -25,11 +27,11 @@ class Licensee
25
27
 
26
28
  class << self
27
29
 
28
- attr_writer :confidence_threshold
30
+ attr_writer :confidence_threshold, :package_manager_files
29
31
 
30
32
  # Returns an array of Licensee::License instances
31
33
  def licenses
32
- @licenses ||= Licensee::Licenses.list
34
+ @licenses ||= Licensee::License.all
33
35
  end
34
36
 
35
37
  # Returns the license for a given git repo
@@ -45,11 +47,24 @@ class Licensee
45
47
  # Array of matchers to use, in order of preference
46
48
  # The order should be decending order of anticipated speed to match
47
49
  def matchers
48
- [Licensee::CopyrightMatcher, Licensee::ExactMatcher, Licensee::GitMatcher, Licensee::LevenshteinMatcher]
50
+ matchers = [
51
+ Licensee::CopyrightMatcher,
52
+ Licensee::ExactMatcher,
53
+ Licensee::GitMatcher,
54
+ Licensee::LevenshteinMatcher,
55
+ Licensee::GemspecMatcher,
56
+ Licensee::NpmBowerMatcher
57
+ ]
58
+ matchers.reject! { |m| m.package_manager? } unless package_manager_files?
59
+ matchers
49
60
  end
50
61
 
51
62
  def confidence_threshold
52
63
  @confidence_threshold ||= CONFIDENCE_THRESHOLD
53
64
  end
65
+
66
+ def package_manager_files?
67
+ @package_manager_files ||= false
68
+ end
54
69
  end
55
70
  end
@@ -2,16 +2,36 @@ class Licensee
2
2
  class InvalidLicense < ArgumentError; end
3
3
  class License
4
4
 
5
- def self.all
6
- Licensee::licenses
5
+ class << self
6
+ def all
7
+ @all ||= keys.map { |key| self.new(key) }
8
+ end
9
+
10
+ def keys
11
+ @keys ||= license_files.map { |l| File.basename(l, ".txt").downcase }
12
+ end
13
+
14
+ def find(key)
15
+ key = key.downcase
16
+ all.find { |l| l.key == key }
17
+ end
18
+ alias_method :[], :find
19
+
20
+ def license_dir
21
+ File.expand_path "../../vendor/choosealicense.com/_licenses", File.dirname(__FILE__)
22
+ end
23
+
24
+ def license_files
25
+ @license_files ||= Dir.glob("#{license_dir}/*.txt")
26
+ end
7
27
  end
8
28
 
9
29
  attr_reader :key
10
30
 
11
31
  YAML_DEFAULTS = {
12
32
  "featured" => false,
13
- "hidden" => false,
14
- "variant" => false
33
+ "hidden" => false,
34
+ "variant" => false
15
35
  }
16
36
 
17
37
  HIDDEN_LICENSES = %w[other no-license]
@@ -22,7 +42,7 @@ class Licensee
22
42
 
23
43
  # Path to vendored license file on disk
24
44
  def path
25
- @path ||= File.expand_path "#{@key}.txt", Licensee::Licenses.base
45
+ @path ||= File.expand_path "#{@key}.txt", Licensee::License.license_dir
26
46
  end
27
47
 
28
48
  # Raw content of license file, including YAML front matter
@@ -12,6 +12,10 @@ class Licensee
12
12
  self.new(file).match
13
13
  end
14
14
 
15
+ def self.package_manager?
16
+ false
17
+ end
18
+
15
19
  def initialize(file)
16
20
  @file = file
17
21
  end
@@ -17,7 +17,7 @@ class Licensee
17
17
  private
18
18
 
19
19
  def no_license
20
- @no_license ||= Licensee::Licenses.find("no-license")
20
+ @no_license ||= Licensee::License.find("no-license")
21
21
  end
22
22
  end
23
23
  end
@@ -0,0 +1,16 @@
1
+ class Licensee
2
+ class GemspecMatcher < PackageMatcher
3
+
4
+ # We definitely don't want to be evaling arbitrary Gemspec files
5
+ # While not 100% accurate, use some lenient regex to try to grep the
6
+ # license declaration from the Gemspec as a string, if any
7
+ LICENSE_REGEX = /^\s*[a-z0-9_]+\.license\s*\=\s*[\'\"]([a-z\-0-9\.]+)[\'\"]\s*$/i
8
+
9
+ private
10
+
11
+ def license_property
12
+ match = file.content.match LICENSE_REGEX
13
+ match[1].downcase if match && match[1]
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,15 @@
1
+ class Licensee
2
+ class NpmBowerMatcher < PackageMatcher
3
+
4
+ # While we could parse the package.json or bower.json file, prefer
5
+ # a lenient regex for speed and security. Moar parsing moar problems.
6
+ LICENSE_REGEX = /\s*[\"\']license[\"\']\s*\:\s*[\'\"]([a-z\-0-9\.]+)[\'\"],?\s*/i
7
+
8
+ private
9
+
10
+ def license_property
11
+ match = file.content.match LICENSE_REGEX
12
+ match[1].downcase if match && match[1]
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,16 @@
1
+ class Licensee
2
+ class PackageMatcher < Matcher
3
+
4
+ def match
5
+ Licensee.licenses.find { |l| l.key == license_property } if file.package?
6
+ end
7
+
8
+ def confidence
9
+ 90
10
+ end
11
+
12
+ def self.package_manager?
13
+ true
14
+ end
15
+ end
16
+ end
@@ -1,6 +1,6 @@
1
1
  class Licensee
2
2
  class Project
3
- attr_reader :repository
3
+ attr_reader :repository, :revision
4
4
 
5
5
  # Initializes a new project
6
6
  #
@@ -13,66 +13,44 @@ class Licensee
13
13
  begin
14
14
  @repository = Rugged::Repository.new(path_or_repo)
15
15
  rescue Rugged::RepositoryError
16
- if revision
17
- raise
18
- else
19
- @repository = FilesystemRepository.new(path_or_repo)
20
- end
16
+ raise if revision
17
+ @repository = FilesystemRepository.new(path_or_repo)
21
18
  end
22
19
  end
23
20
 
24
21
  @revision = revision
25
22
  end
26
23
 
27
- # Returns an instance of Licensee::LicenseFile if there's a license file detected
24
+ # Returns the matching Licensee::License instance if a license can be detected
25
+ def license
26
+ @license ||= matched_file.match if matched_file
27
+ end
28
+
28
29
  def license_file
29
- @license_file ||= LicenseFile.new(license_blob, :path => license_path) if license_blob
30
+ @license_file ||= files.select { |f| f.license? }.sort_by { |f| f.license_score }.last
30
31
  end
31
32
 
32
- # Returns the matching Licensee::License instance if a license can be detected
33
- def license
34
- @license ||= license_file.match if license_file
33
+ def package_file
34
+ return unless Licensee.package_manager_files?
35
+ @package_file ||= files.select { |f| f.package? }.sort_by { |f| f.package_score }.last
35
36
  end
36
37
 
37
- # Scores a given file as a potential license
38
- #
39
- # filename - (string) the name of the file to score
40
- #
41
- # Returns 1.0 if the file is definitely a license file
42
- # Returns 0.75 if the file is probably a license file
43
- # Returns 0.5 if the file is likely a license file
44
- # Returns 0.0 if the file is definitely not a license file
45
- def self.match_license_file(filename)
46
- return 1.0 if filename =~ /\A(un)?licen[sc]e(\.[^.]+)?\z/i
47
- return 0.75 if filename =~ /\Acopy(ing|right)(\.[^.]+)?\z/i
48
- return 0.5 if filename =~ /licen[sc]e/i
49
- return 0.0
38
+ def matched_file
39
+ license_file || package_file
50
40
  end
51
41
 
52
42
  private
53
43
 
54
44
  def commit
55
- @revision ? @repository.lookup(@revision) : @repository.last_commit
45
+ @commit ||= revision ? repository.lookup(revision) : repository.last_commit
56
46
  end
57
47
 
58
48
  def tree
59
- commit.tree.select { |blob| blob[:type] == :blob }
60
- end
61
-
62
- # Detects the license file, if any
63
- # Returns the blob hash as detected in the tree
64
- def license_hash
65
- hashes = tree.map { |blob| [self.class.match_license_file(blob[:name]), blob] }
66
- hash = hashes.select { |hash| hash[0] > 0 }.sort_by { |hash| hash[0] }.last
67
- hash[1] if hash
68
- end
69
-
70
- def license_blob
71
- @repository.lookup(license_hash[:oid]) if license_hash
49
+ @tree ||= commit.tree.select { |blob| blob[:type] == :blob }
72
50
  end
73
51
 
74
- def license_path
75
- license_hash[:name] if license_hash
52
+ def files
53
+ @files ||= tree.map { |blob| ProjectFile.new(repository.lookup(blob[:oid]), blob[:name]) }
76
54
  end
77
55
  end
78
56
  end
@@ -0,0 +1,91 @@
1
+ class Licensee
2
+ class ProjectFile
3
+
4
+ # Note: File can be a license file (e.g., `LICENSE.txt`)
5
+ # or a package manager file (e.g, `package.json`)
6
+
7
+ attr_reader :blob, :path
8
+ alias_method :filename, :path
9
+
10
+ def initialize(blob, path)
11
+ @blob = blob
12
+ @path = path
13
+ end
14
+
15
+ # Raw file contents
16
+ def content
17
+ @contents ||= blob.content.force_encoding("UTF-8")
18
+ end
19
+ alias_method :to_s, :content
20
+ alias_method :contents, :content
21
+
22
+ # File content with all whitespace replaced with a single space
23
+ def content_normalized
24
+ @content_normalized ||= content.downcase.gsub(/\s+/, " ").strip
25
+ end
26
+
27
+ # Determines which matching strategy to use, returns an instane of that matcher
28
+ def matcher
29
+ @matcher ||= Licensee.matchers.map { |m| m.new(self) }.find { |m| m.match }
30
+ end
31
+
32
+ # Returns an Licensee::License instance of the matches license
33
+ def match
34
+ @match ||= matcher.match if matcher
35
+ end
36
+
37
+ # Returns the percent confident with the match
38
+ def confidence
39
+ @condience ||= matcher.confidence if matcher
40
+ end
41
+
42
+ def similarity(other)
43
+ blob.hashsig(Rugged::Blob::HashSignature::WHITESPACE_SMART)
44
+ other.hashsig ? blob.similarity(other.hashsig) : 0
45
+ rescue Rugged::InvalidError
46
+ 0
47
+ end
48
+
49
+ # Comptutes a diff between known license and project license
50
+ def diff(options={})
51
+ options = options.merge(:reverse => true)
52
+ blob.diff(match.body, options).to_s if match
53
+ end
54
+
55
+ def license_score
56
+ self.class.license_score(filename)
57
+ end
58
+
59
+ def license?
60
+ license_score != 0.0
61
+ end
62
+
63
+ def package_score
64
+ return 1.0 if filename =~ /[a-zA-Z0-9\-_]+\.gemspec/
65
+ return 1.0 if filename =~ /package\.json/
66
+ return 0.75 if filename =~ /bower.json/
67
+ return 0.0
68
+ end
69
+
70
+ def package?
71
+ Licensee.package_manager_files? && package_score != 0.0
72
+ end
73
+
74
+ class << self
75
+ # Scores a given file as a potential license
76
+ #
77
+ # filename - (string) the name of the file to score
78
+ #
79
+ # Returns 1.0 if the file is definitely a license file
80
+ # Returns 0.75 if the file is probably a license file
81
+ # Returns 0.5 if the file is likely a license file
82
+ # Returns 0.0 if the file is definitely not a license file
83
+ def license_score(filename)
84
+ return 1.0 if filename =~ /\A(un)?licen[sc]e(\.[^.]+)?\z/i
85
+ return 0.75 if filename =~ /\Acopy(ing|right)(\.[^.]+)?\z/i
86
+ return 0.5 if filename =~ /licen[sc]e/i
87
+ return 0.0
88
+ end
89
+ end
90
+ end
91
+ end
@@ -1,3 +1,3 @@
1
1
  class Licensee
2
- VERSION = "4.9.0"
2
+ VERSION = "5.0.0b1"
3
3
  end
@@ -0,0 +1,3 @@
1
+ {
2
+ "license": "mit"
3
+ }
@@ -0,0 +1,3 @@
1
+ {
2
+ "license": "mit-1.0"
3
+ }
@@ -0,0 +1,3 @@
1
+ {
2
+ "license": "mit"
3
+ }
data/test/functions.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  # Pulled from helper.rb because something in the test suite monkey patches benchmarking
2
2
 
3
3
  require 'securerandom'
4
- require 'licensee/filesystem_repository'
4
+ require_relative '../lib/licensee'
5
5
 
6
6
  def fixtures_base
7
7
  File.expand_path "fixtures", File.dirname( __FILE__ )
@@ -36,7 +36,7 @@ def verify_license_file(license, chaos = false, wrap=false)
36
36
  text = wrap(text, wrap) if wrap
37
37
 
38
38
  blob = FakeBlob.new(text)
39
- license_file = Licensee::LicenseFile.new(blob)
39
+ license_file = Licensee::ProjectFile.new(blob, "LICENSE")
40
40
 
41
41
  actual = license_file.match
42
42
  assert actual, "No match for #{expected}. Here's the test text:\n#{text}"
data/test/helper.rb CHANGED
@@ -4,7 +4,4 @@ require 'minitest/autorun'
4
4
  require 'shoulda'
5
5
  require 'open3'
6
6
  require_relative 'functions'
7
-
8
- $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
9
-
10
- require 'licensee'
7
+ require_relative '../lib/licensee'
@@ -15,13 +15,27 @@ class TestLicensee < Minitest::Test
15
15
  Licensee.diff(fixture_path("licenses.git"))
16
16
  end
17
17
 
18
- should "return the confidence threshold" do
19
- assert_equal 90, Licensee.confidence_threshold
18
+ context "confidence threshold" do
19
+ should "return the confidence threshold" do
20
+ assert_equal 90, Licensee.confidence_threshold
21
+ end
22
+
23
+ should "let the user override the confidence threshold" do
24
+ Licensee.confidence_threshold = 50
25
+ assert_equal 50, Licensee.confidence_threshold
26
+ Licensee.confidence_threshold = 90
27
+ end
20
28
  end
21
29
 
22
- should "let the user override the confidence threshold" do
23
- Licensee.confidence_threshold = 50
24
- assert_equal 50, Licensee.confidence_threshold
25
- Licensee.confidence_threshold = 90
30
+ context "npm-bower matcher" do
31
+ should "be disabled by default" do
32
+ refute Licensee.matchers.include? Licensee::NpmBowerMatcher
33
+ end
34
+
35
+ should "be enable-able" do
36
+ Licensee.package_manager_files = true
37
+ assert Licensee.matchers.include? Licensee::NpmBowerMatcher
38
+ Licensee.package_manager_files = false
39
+ end
26
40
  end
27
41
  end
@@ -6,7 +6,7 @@ class TestLicenseeBin < Minitest::Test
6
6
  Dir.chdir root
7
7
  stdout,stderr,status = Open3.capture3("#{root}/bin/licensee")
8
8
  assert stdout.include?("License: MIT"), "expected #{stdout} to include `License: MIT`"
9
- assert stdout.include?("License file: LICENSE.md"), "expected #{stdout} to include `License file: LICENSE.md`"
9
+ assert stdout.include?("Matched file: LICENSE.md"), "expected #{stdout} to include `Matched file: LICENSE.md`"
10
10
  assert_equal 0, status
11
11
  end
12
12
  end
@@ -6,7 +6,7 @@ class TestLicenseeCopyrightMatcher < Minitest::Test
6
6
  def setup
7
7
  text = "Copyright 2015 Ben Balter"
8
8
  blob = FakeBlob.new(text)
9
- @file = Licensee::LicenseFile.new(blob)
9
+ @file = Licensee::ProjectFile.new(blob, "LICENSE")
10
10
  end
11
11
 
12
12
  should "match the license" do
@@ -20,35 +20,35 @@ class TestLicenseeCopyrightMatcher < Minitest::Test
20
20
  should "match Copyright (C) copyright notices" do
21
21
  text = "Copyright (C) 2015 Ben Balter"
22
22
  blob = FakeBlob.new(text)
23
- file = Licensee::LicenseFile.new(blob)
23
+ file = Licensee::ProjectFile.new(blob, "LICENSE")
24
24
  assert_equal "no-license", Licensee::CopyrightMatcher.match(file).key
25
25
  end
26
26
 
27
27
  should "match Copyright © copyright notices" do
28
28
  text = "copyright © 2015 Ben Balter"
29
29
  blob = FakeBlob.new(text)
30
- file = Licensee::LicenseFile.new(blob)
30
+ file = Licensee::ProjectFile.new(blob, "LICENSE")
31
31
  assert_equal "no-license", Licensee::CopyrightMatcher.match(file).key
32
32
  end
33
33
 
34
34
  should "not false positive" do
35
- text = File.open(Licensee::Licenses.find("mit").path).read.split("---").last
35
+ text = File.open(Licensee::License.find("mit").path).read.split("---").last
36
36
  blob = FakeBlob.new(text)
37
- file = Licensee::LicenseFile.new(blob)
37
+ file = Licensee::ProjectFile.new(blob, "LICENSE")
38
38
  assert_equal nil, Licensee::CopyrightMatcher.match(file)
39
39
  end
40
40
 
41
41
  should "handle UTF-8 encoded copyright notices" do
42
42
  text = "Copyright (c) 2010-2014 Simon Hürlimann"
43
43
  blob = FakeBlob.new(text)
44
- file = Licensee::LicenseFile.new(blob)
44
+ file = Licensee::ProjectFile.new(blob, "LICENSE")
45
45
  assert_equal "no-license", Licensee::CopyrightMatcher.match(file).key
46
46
  end
47
47
 
48
48
  should "handle ASCII-8BIT encoded copyright notices" do
49
49
  text = "Copyright \xC2\xA92015 Ben Balter`".force_encoding("ASCII-8BIT")
50
50
  blob = FakeBlob.new(text)
51
- file = Licensee::LicenseFile.new(blob)
51
+ file = Licensee::ProjectFile.new(blob, "LICENSE")
52
52
  assert_equal "no-license", Licensee::CopyrightMatcher.match(file).key
53
53
  end
54
54
  end
@@ -3,9 +3,9 @@ require 'helper'
3
3
  class TestLicenseeExactMatcher < Minitest::Test
4
4
 
5
5
  def setup
6
- text = File.open(Licensee::Licenses.find("mit").path).read.split("---").last
6
+ text = File.open(Licensee::License.find("mit").path).read.split("---").last
7
7
  blob = FakeBlob.new(text)
8
- @mit = Licensee::LicenseFile.new(blob)
8
+ @mit = Licensee::ProjectFile.new(blob, "LICENSE")
9
9
  end
10
10
 
11
11
  should "match the license" do
@@ -0,0 +1,19 @@
1
+ require 'helper'
2
+
3
+ class TestLicenseeGemspecMatcher < Minitest::Test
4
+ def setup
5
+ Licensee.package_manager_files = true
6
+ end
7
+
8
+ def teardown
9
+ Licensee.package_manager_files = false
10
+ end
11
+
12
+ should "detect its own license" do
13
+ root = File.expand_path "../", File.dirname(__FILE__)
14
+ project = Licensee::Project.new(root)
15
+ matcher = Licensee::GemspecMatcher.new(project.package_file)
16
+ assert_equal "mit", matcher.send(:license_property)
17
+ assert_equal "mit", matcher.match.key
18
+ end
19
+ end
@@ -3,9 +3,9 @@ require 'helper'
3
3
  class TestLicenseeGitMatcher < Minitest::Test
4
4
 
5
5
  def setup
6
- text = license_from_path( Licensee::Licenses.find("mit").path )
6
+ text = license_from_path( Licensee::License.find("mit").path )
7
7
  blob = FakeBlob.new(text)
8
- @mit = Licensee::LicenseFile.new(blob)
8
+ @mit = Licensee::ProjectFile.new(blob, "LICENSE")
9
9
  end
10
10
 
11
11
  should "match the license" do
@@ -3,9 +3,9 @@ require 'helper'
3
3
  class TestLicenseeLevenshteinMatcher < Minitest::Test
4
4
 
5
5
  def setup
6
- text = license_from_path( Licensee::Licenses.find("mit").path )
6
+ text = license_from_path( Licensee::License.find("mit").path )
7
7
  blob = FakeBlob.new(text)
8
- @mit = Licensee::LicenseFile.new(blob)
8
+ @mit = Licensee::ProjectFile.new(blob, "LICENSE")
9
9
  end
10
10
 
11
11
  should "match the license" do
@@ -22,8 +22,8 @@ class TestLicenseeLevenshteinMatcher < Minitest::Test
22
22
  end
23
23
 
24
24
  should "calculate length delta" do
25
- isc = Licensee::Licenses.find("isc")
26
- assert_equal 2, Licensee::LevenshteinMatcher.new(@mit).length_delta(Licensee::Licenses.find("mit"))
25
+ isc = Licensee::License.find("isc")
26
+ assert_equal 2, Licensee::LevenshteinMatcher.new(@mit).length_delta(Licensee::License.find("mit"))
27
27
  assert_equal 334, Licensee::LevenshteinMatcher.new(@mit).length_delta(isc)
28
28
  end
29
29
 
@@ -88,4 +88,23 @@ class TestLicenseeLicense < Minitest::Test
88
88
  assert_equal "Other", license.name
89
89
  refute license.featured?
90
90
  end
91
+
92
+ describe "class methods" do
93
+ should "know license names" do
94
+ assert_equal Array, Licensee::License.keys.class
95
+ assert_equal 19, Licensee::License.keys.size
96
+ end
97
+
98
+ should "load the licenses" do
99
+ assert_equal Array, Licensee::License.all.class
100
+ assert_equal 19, Licensee::License.all.size
101
+ assert_equal Licensee::License, Licensee::License.all.first.class
102
+ end
103
+
104
+ should "find a license" do
105
+ assert_equal "mit", Licensee::License.find("mit").key
106
+ assert_equal "mit", Licensee::License.find("MIT").key
107
+ assert_equal "mit", Licensee::License["mit"].key
108
+ end
109
+ end
91
110
  end
@@ -0,0 +1,33 @@
1
+ require 'helper'
2
+
3
+ class TestLicenseeNpmBowerMatcher < Minitest::Test
4
+
5
+ def setup
6
+ Licensee.package_manager_files = true
7
+ end
8
+
9
+ def teardown
10
+ Licensee.package_manager_files = false
11
+ end
12
+
13
+ should "detect NPM files" do
14
+ project = Licensee::Project.new fixture_path "npm"
15
+ matcher = Licensee::NpmBowerMatcher.new(project.package_file)
16
+ assert_equal "mit", matcher.send(:license_property)
17
+ assert_equal "mit", matcher.match.key
18
+ end
19
+
20
+ should "detect Bower files" do
21
+ project = Licensee::Project.new fixture_path "bower"
22
+ matcher = Licensee::NpmBowerMatcher.new(project.package_file)
23
+ assert_equal "mit", matcher.send(:license_property)
24
+ assert_equal "mit", matcher.match.key
25
+ end
26
+
27
+ should "not err on non-spdx licenses" do
28
+ project = Licensee::Project.new fixture_path "npm-non-spdx"
29
+ matcher = Licensee::NpmBowerMatcher.new(project.package_file)
30
+ assert_equal "mit-1.0", matcher.send(:license_property)
31
+ assert_equal nil, matcher.match
32
+ end
33
+ end
@@ -28,7 +28,7 @@ class TestLicenseeProject < Minitest::Test
28
28
 
29
29
  should "detect the license file" do
30
30
  project = make_project "licenses.git", as_git
31
- assert_instance_of Licensee::LicenseFile, project.license_file
31
+ assert_instance_of Licensee::ProjectFile, project.license_file
32
32
  end
33
33
 
34
34
  should "detect the license" do
@@ -36,24 +36,9 @@ class TestLicenseeProject < Minitest::Test
36
36
  assert_equal "mit", project.license.key
37
37
  end
38
38
 
39
- should "return the license hash" do
40
- project = make_project "licenses.git", as_git
41
- assert_equal "LICENSE", project.send(:license_hash)[:name]
42
- end
43
-
44
- should "return the license blob" do
45
- project = make_project "licenses.git", as_git
46
- assert_equal 1077, project.send(:license_blob).size
47
- end
48
-
49
- should "return the license path" do
50
- project = make_project "licenses.git", as_git
51
- assert_equal "LICENSE", project.send(:license_path)
52
- end
53
-
54
39
  should "detect an atypically cased license file" do
55
40
  project = make_project "case-sensitive.git", as_git
56
- assert_instance_of Licensee::LicenseFile, project.license_file
41
+ assert_instance_of Licensee::ProjectFile, project.license_file
57
42
  end
58
43
 
59
44
  should "detect MIT-LICENSE licensed projects" do
@@ -83,29 +68,20 @@ class TestLicenseeProject < Minitest::Test
83
68
  end
84
69
  end
85
70
 
86
- context "license filename scoring" do
87
-
88
- EXPECTATIONS = {
89
- "license" => 1.0,
90
- "LICENCE" => 1.0,
91
- "license.md" => 1.0,
92
- "LICENSE.md" => 1.0,
93
- "license.txt" => 1.0,
94
- "unLICENSE" => 1.0,
95
- "unlicence" => 1.0,
96
- "COPYING" => 0.75,
97
- "copyRIGHT" => 0.75,
98
- "COPYRIGHT.txt" => 0.75,
99
- "LICENSE-MIT" => 0.5,
100
- "MIT-LICENSE.txt" => 0.5,
101
- "mit-license-foo.md" => 0.5,
102
- "README.txt" => 0.0
103
- }
104
-
105
- EXPECTATIONS.each do |filename, expected|
106
- should "score a license named `#{filename}` as `#{expected}`" do
107
- assert_equal expected, Licensee::Project.match_license_file(filename)
108
- end
71
+ describe "packages" do
72
+
73
+ def setup
74
+ Licensee.package_manager_files = true
75
+ end
76
+
77
+ def teardown
78
+ Licensee.package_manager_files = false
79
+ end
80
+
81
+ should "detect a package file" do
82
+ project = Licensee::Project.new fixture_path "npm"
83
+ assert_equal "package.json", project.package_file.filename
84
+ assert_equal "mit", project.license.key
109
85
  end
110
86
  end
111
87
  end
@@ -0,0 +1,59 @@
1
+ require 'helper'
2
+
3
+ class TestLicenseeProjectFile < Minitest::Test
4
+
5
+ def setup
6
+ @repo = Rugged::Repository.new(fixture_path("licenses.git"))
7
+ blob = 'bcb552d06d9cf1cd4c048a6d3bf716849c2216cc'
8
+ @file = Licensee::ProjectFile.new(@repo.lookup(blob), "LICENSE")
9
+ @gpl = Licensee::License.find "GPL-3.0"
10
+ @mit = Licensee::License.find "MIT"
11
+ end
12
+
13
+ should "read the file" do
14
+ assert @file.contents =~ /MIT/
15
+ end
16
+
17
+ should "match the license" do
18
+ assert_equal "mit", @file.match.key
19
+ end
20
+
21
+ should "know the path" do
22
+ assert_equal "LICENSE", @file.path
23
+ end
24
+
25
+ should "diff the file" do
26
+ expected = "-Copyright (c) [year] [fullname]\n+Copyright (c) 2014 Ben Balter"
27
+ assert @file.diff.include?(expected)
28
+ end
29
+
30
+ should "calculate confidence" do
31
+ assert_equal 94, @file.confidence
32
+ end
33
+
34
+ context "license filename scoring" do
35
+
36
+ EXPECTATIONS = {
37
+ "license" => 1.0,
38
+ "LICENCE" => 1.0,
39
+ "license.md" => 1.0,
40
+ "LICENSE.md" => 1.0,
41
+ "license.txt" => 1.0,
42
+ "unLICENSE" => 1.0,
43
+ "unlicence" => 1.0,
44
+ "COPYING" => 0.75,
45
+ "copyRIGHT" => 0.75,
46
+ "COPYRIGHT.txt" => 0.75,
47
+ "LICENSE-MIT" => 0.5,
48
+ "MIT-LICENSE.txt" => 0.5,
49
+ "mit-license-foo.md" => 0.5,
50
+ "README.txt" => 0.0
51
+ }
52
+
53
+ EXPECTATIONS.each do |filename, expected|
54
+ should "score a license named `#{filename}` as `#{expected}`" do
55
+ assert_equal expected, Licensee::ProjectFile.license_score(filename)
56
+ end
57
+ end
58
+ end
59
+ end
@@ -1,7 +1,7 @@
1
1
  require 'helper'
2
2
 
3
3
  class TestLicenseeVendor < Minitest::Test
4
- Dir["#{Licensee::Licenses.base}/*"].shuffle.each do |license|
4
+ Licensee::License.send(:license_files).shuffle.each do |license|
5
5
 
6
6
  should "detect the #{license} license" do
7
7
  verify_license_file(license)
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: licensee
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.9.0
4
+ version: 5.0.0b1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ben Balter
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-08-22 00:00:00.000000000 Z
11
+ date: 2015-08-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rugged
@@ -109,15 +109,18 @@ files:
109
109
  - lib/licensee.rb
110
110
  - lib/licensee/filesystem_repository.rb
111
111
  - lib/licensee/license.rb
112
- - lib/licensee/license_file.rb
113
- - lib/licensee/licenses.rb
114
112
  - lib/licensee/matcher.rb
115
113
  - lib/licensee/matchers/copyright_matcher.rb
116
114
  - lib/licensee/matchers/exact_matcher.rb
115
+ - lib/licensee/matchers/gemspec_matcher.rb
117
116
  - lib/licensee/matchers/git_matcher.rb
118
117
  - lib/licensee/matchers/levenshtein_matcher.rb
118
+ - lib/licensee/matchers/npm_bower_matcher.rb
119
+ - lib/licensee/matchers/package_matcher.rb
119
120
  - lib/licensee/project.rb
121
+ - lib/licensee/project_file.rb
120
122
  - lib/licensee/version.rb
123
+ - test/fixtures/bower/bower.json
121
124
  - test/fixtures/case-sensitive.git/HEAD
122
125
  - test/fixtures/case-sensitive.git/config
123
126
  - test/fixtures/case-sensitive.git/objects/01/7b4f1eebd1dcb735e950b1d01093e3e2bf85e9
@@ -168,19 +171,22 @@ files:
168
171
  - test/fixtures/no-license.git/objects/e1/d9b2a3d41c2ea74a520e66da2b5c63b2f6202f
169
172
  - test/fixtures/no-license.git/objects/ff/1592f44259635df9feda5e02853964b26f9e4d
170
173
  - test/fixtures/no-license.git/refs/heads/master
174
+ - test/fixtures/npm-non-spdx/package.json
175
+ - test/fixtures/npm/package.json
171
176
  - test/functions.rb
172
177
  - test/helper.rb
173
178
  - test/test_licensee.rb
174
179
  - test/test_licensee_bin.rb
175
180
  - test/test_licensee_copyright_matcher.rb
176
181
  - test/test_licensee_exact_matcher.rb
182
+ - test/test_licensee_gemspec_matcher.rb
177
183
  - test/test_licensee_git_matcher.rb
178
184
  - test/test_licensee_levenshtein_matcher.rb
179
185
  - test/test_licensee_license.rb
180
- - test/test_licensee_license_file.rb
181
- - test/test_licensee_licenses.rb
182
186
  - test/test_licensee_matcher.rb
187
+ - test/test_licensee_npm_bower_matcher.rb
183
188
  - test/test_licensee_project.rb
189
+ - test/test_licensee_project_file.rb
184
190
  - test/test_licensee_vendor.rb
185
191
  - vendor/choosealicense.com/_licenses/agpl-3.0.txt
186
192
  - vendor/choosealicense.com/_licenses/apache-2.0.txt
@@ -216,9 +222,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
216
222
  version: '0'
217
223
  required_rubygems_version: !ruby/object:Gem::Requirement
218
224
  requirements:
219
- - - ">="
225
+ - - ">"
220
226
  - !ruby/object:Gem::Version
221
- version: '0'
227
+ version: 1.3.1
222
228
  requirements: []
223
229
  rubyforge_project:
224
230
  rubygems_version: 2.4.8
@@ -1,52 +0,0 @@
1
- class Licensee
2
- class LicenseFile
3
- attr_reader :blob, :path
4
-
5
- def initialize(blob, options={})
6
- @blob = blob
7
- @path = options[:path]
8
- end
9
-
10
- def similarity(other)
11
- blob.hashsig(Rugged::Blob::HashSignature::WHITESPACE_SMART)
12
- other.hashsig ? blob.similarity(other.hashsig) : 0
13
- rescue Rugged::InvalidError
14
- 0
15
- end
16
-
17
- # Raw file contents
18
- def content
19
- @contents ||= begin
20
- blob.content.force_encoding("UTF-8")
21
- end
22
- end
23
- alias_method :to_s, :content
24
- alias_method :contents, :content
25
-
26
- # File content with all whitespace replaced with a single space
27
- def content_normalized
28
- @content_normalized ||= content.downcase.gsub(/\s+/, " ").strip
29
- end
30
-
31
- # Comptutes a diff between known license and project license
32
- def diff(options={})
33
- options = options.merge(:reverse => true)
34
- blob.diff(match.body, options).to_s if match
35
- end
36
-
37
- # Determines which matching strategy to use, returns an instane of that matcher
38
- def matcher
39
- @matcher ||= Licensee.matchers.map { |m| m.new(self) }.find { |m| m.match }
40
- end
41
-
42
- # Returns an Licensee::License instance of the matches license
43
- def match
44
- @match ||= matcher.match if matcher
45
- end
46
-
47
- # Returns the percent confident with the match
48
- def confidence
49
- @condience ||= matcher.confidence if matcher
50
- end
51
- end
52
- end
@@ -1,36 +0,0 @@
1
- class Licensee
2
- class Licenses
3
- class << self
4
-
5
- # Returns an array of Licensee::License instances
6
- def list
7
- @licenses ||= begin
8
- licenses = []
9
- keys.each { |key| licenses.push License.new(key) }
10
- licenses
11
- end
12
- end
13
-
14
- # Given a license key, attempt to return a matching Licensee::License instance
15
- def find(key)
16
- list.find { |l| l.key.downcase == key.downcase }
17
- end
18
- alias_method :[], :find
19
-
20
- # Path to vendored licenses
21
- def base
22
- @base ||= File.expand_path "../../vendor/choosealicense.com/_licenses", File.dirname(__FILE__)
23
- end
24
-
25
- # Returns a list of potential license keys, as vendored
26
- def keys
27
- @keys ||= begin
28
- keyes = Dir.entries(base)
29
- keyes.map! { |l| File.basename(l, ".txt").downcase }
30
- keyes.reject! { |l| l =~ /^\./ || l.nil? }
31
- keyes
32
- end
33
- end
34
- end
35
- end
36
- end
@@ -1,33 +0,0 @@
1
- require 'helper'
2
-
3
- class TestLicenseeLicenseFile < Minitest::Test
4
-
5
- def setup
6
- @repo = Rugged::Repository.new(fixture_path("licenses.git"))
7
- blob = 'bcb552d06d9cf1cd4c048a6d3bf716849c2216cc'
8
- @file = Licensee::LicenseFile.new(@repo.lookup(blob), :path => "LICENSE")
9
- @gpl = Licensee::Licenses.find "GPL-3.0"
10
- @mit = Licensee::Licenses.find "MIT"
11
- end
12
-
13
- should "read the file" do
14
- assert @file.contents =~ /MIT/
15
- end
16
-
17
- should "match the license" do
18
- assert_equal "mit", @file.match.key
19
- end
20
-
21
- should "know the path" do
22
- assert_equal "LICENSE", @file.path
23
- end
24
-
25
- should "diff the file" do
26
- expected = "-Copyright (c) [year] [fullname]\n+Copyright (c) 2014 Ben Balter"
27
- assert @file.diff.include?(expected)
28
- end
29
-
30
- should "calculate confidence" do
31
- assert_equal 94, @file.confidence
32
- end
33
- end
@@ -1,21 +0,0 @@
1
- require 'helper'
2
-
3
- class TestLicenseeLicenses < Minitest::Test
4
-
5
- should "know license names" do
6
- assert_equal Array, Licensee::Licenses.keys.class
7
- assert_equal 19, Licensee::Licenses.keys.size
8
- end
9
-
10
- should "load the licenses" do
11
- assert_equal Array, Licensee::Licenses.list.class
12
- assert_equal 19, Licensee::Licenses.list.size
13
- assert_equal Licensee::License, Licensee::Licenses.list.first.class
14
- end
15
-
16
- should "find a license" do
17
- assert_equal "mit", Licensee::Licenses.find("mit").key
18
- assert_equal "mit", Licensee::Licenses.find("MIT").key
19
- assert_equal "mit", Licensee::Licenses["mit"].key
20
- end
21
- end