gjp 0.6.0 → 0.7.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.
data/README.md CHANGED
@@ -15,8 +15,8 @@ Easiest install is via RubyGems:
15
15
  ## Usage
16
16
 
17
17
  Currently available tools:
18
- * `gjp get-pom JAR` will attempt to find an jar's pom.xml (either from the package itself or through search.maven.org);
19
- * `gjp get-source-address POM` will attempt to find the SCM Internet address of a pom.xml (from the file itself or through api.github.com);
18
+ * `gjp get-pom NAME` will attempt to find a pom. `NAME` can be a jar file on your disk, a project directory, or simply a `name-version` string. `gjp` will get the pom either from the package itself or through search.maven.org using heuristic searching;
19
+ * `gjp get-source-address POM` will attempt to find the SCM Internet address of a pom.xml at the `POM` filename or URI (from the file itself or through api.github.com);
20
20
  * `gjp get-source POM ADDRESS` downloads the source of a pom.xml's project from its SCM at ADDRESS;
21
21
 
22
22
  ## Source
data/bin/gjp CHANGED
@@ -15,11 +15,15 @@
15
15
  # http://www.gnu.org/licenses/lgpl-2.1.html
16
16
  #
17
17
 
18
+ if File.exist?(File.join(File.dirname(__FILE__), "..", ".git"))
19
+ $: << File.join(File.dirname(__FILE__), "..", "lib")
20
+ end
21
+
18
22
  begin
19
- require 'gjp'
23
+ require "gjp"
20
24
  rescue LoadError
21
- require 'rubygems'
22
- require 'gjp'
25
+ require "rubygems"
26
+ require "gjp"
23
27
  end
24
28
 
25
- MainCommand.run
29
+ Gjp::MainCommand.run
data/lib/gjp.rb CHANGED
@@ -1,3 +1,4 @@
1
+ require "gjp/logger"
1
2
  require "gjp/version"
2
3
  require "gjp/logger"
3
4
  require "gjp/cli"
@@ -1,51 +1,68 @@
1
1
  # encoding: UTF-8
2
-
2
+ require "gjp/logger"
3
3
  require "clamp"
4
4
 
5
- class MainCommand < Clamp::Command
6
- subcommand "get-pom", "Retrieves a pom corresponding to a jar" do
7
- parameter "JAR", "jar file path"
8
- option ["-v", "--verbose"], :flag, "verbose output"
9
- option ["--very-verbose"], :flag, "very verbose output"
10
- option ["--very-very-verbose"], :flag, "very very verbose output"
11
-
12
- def execute
13
- begin
14
- init_logger
15
- puts PomGetter.get_pom(jar)
16
- rescue Zip::ZipError
17
- $stderr.puts "#{jar} does not seem to be a valid jar archive, skipping"
18
- rescue TypeError
19
- $stderr.puts "#{jar} seems to be a valid jar archive but is corrupt, skipping"
20
- rescue RestClient::ResourceNotFound
21
- $stderr.puts "Got an error while looking for #{jar} in search.maven.org"
5
+ # Initialize global logger
6
+ Gjp.logger = ::Logger.new(STDERR)
7
+ Gjp.logger.datetime_format = "%Y-%m-%d %H:%M "
8
+ Gjp.logger.level = ::Logger::INFO
9
+ Gjp.logger.formatter = proc do |severity, datetime, progname, msg|
10
+ "#{severity.chars.first}: #{msg}\n"
11
+ end
12
+
13
+ module Gjp
14
+ class MainCommand < Clamp::Command
15
+ subcommand "get-pom", "Retrieves a pom corresponding to a filename" do
16
+ parameter "NAME", "a jar file path, a project directory path or a non-existing filename in the `project-version` form"
17
+ option ["-v", "--verbose"], :flag, "verbose output"
18
+ option ["--very-verbose"], :flag, "very verbose output"
19
+ option ["--very-very-verbose"], :flag, "very very verbose output"
20
+
21
+ def configure_log_level(v, vv, vvv)
22
+ if vvv
23
+ Gjp.logger.level = Logger::DEBUG
24
+ elsif vv
25
+ Gjp.logger.level = Logger::INFO
26
+ elsif v
27
+ Gjp.logger.level = Logger::WARN
28
+ else
29
+ Gjp.logger.level = Logger::ERROR
30
+ end
31
+ end
32
+
33
+ def very_very_verbose=(flag)
34
+ configure_log_level(verbose?, very_verbose?, flag)
35
+ end
36
+
37
+ def very_verbose=(flag)
38
+ configure_log_level(verbose?, flag, very_very_verbose?)
39
+ end
40
+
41
+ def verbose=(flag)
42
+ configure_log_level(flag, very_verbose?, very_very_verbose?)
43
+ end
44
+
45
+ def execute
46
+ puts Gjp::PomGetter.get_pom(name)
22
47
  end
23
48
  end
24
- end
49
+
50
+ subcommand "get-source-address", "Retrieves a project's SCM Internet address" do
51
+ parameter "POM", "project's pom file path"
52
+
53
+ def execute
54
+ puts Gjp::SourceAddressGetter.get_source_address(pom)
55
+ end
56
+ end
25
57
 
26
- subcommand "get-source-address", "Retrieves a project's SCM Internet address" do
27
- parameter "POM", "project's pom file path"
28
- option ["-v", "--verbose"], :flag, "verbose output"
29
- option ["--very-verbose"], :flag, "very verbose output"
30
- option ["--very-very-verbose"], :flag, "very very verbose output"
31
-
32
- def execute
33
- init_logger
34
- puts SourceAddressGetter.get_source_address(pom)
35
- end
36
- end
37
-
38
- subcommand "get-source", "Retrieves a project's source code directory" do
39
- parameter "ADDRESS", "project's SCM Internet address"
40
- parameter "POM", "project's pom file path"
41
- parameter "[DIRECTORY]", "directory in which to save the source code", :default => "."
42
- option ["-v", "--verbose"], :flag, "verbose output"
43
- option ["--very-verbose"], :flag, "very verbose output"
44
- option ["--very-very-verbose"], :flag, "very very verbose output"
45
-
46
- def execute
47
- init_logger
48
- puts SourceGetter.get_source(address, pom, directory)
49
- end
58
+ subcommand "get-source", "Retrieves a project's source code directory" do
59
+ parameter "ADDRESS", "project's SCM Internet address"
60
+ parameter "POM", "project's pom file path"
61
+ parameter "[DIRECTORY]", "directory in which to save the source code", :default => "."
62
+
63
+ def execute
64
+ puts Gjp::SourceGetter.get_source(address, pom, directory)
65
+ end
66
+ end
50
67
  end
51
68
  end
@@ -8,81 +8,100 @@ require "pathname"
8
8
 
9
9
  require "gjp/version_matcher"
10
10
 
11
- # implements the get-pom subcommand
12
- class PomGetter
11
+ module Gjp
12
+ # implements the get-pom subcommand
13
+ class PomGetter
13
14
 
14
- # returns the pom corresponding to a file or directory, if it can be found
15
- def self.get_pom(file)
16
- (get_pom_from_dir(file) or get_pom_from_jar(file) or get_pom_from_sha1(file) or get_pom_from_heuristic(file))
17
- end
15
+ def self.log
16
+ Gjp.logger
17
+ end
18
+
19
+ # returns the pom corresponding to a filename
20
+ def self.get_pom(filename)
21
+ (get_pom_from_dir(filename) or get_pom_from_jar(filename) or get_pom_from_sha1(filename) or get_pom_from_heuristic(filename))
22
+ end
18
23
 
19
- # returns the pom in a project directory
20
- def self.get_pom_from_dir(dir)
21
- if File.directory?(dir)
22
- pom_path = File.join(dir, "pom.xml")
23
- if File.file?(pom_path)
24
- $log.info("pom.xml found in #{dir}/pom.xml")
24
+ # returns the pom in a project directory
25
+ def self.get_pom_from_dir(dir)
26
+ if File.directory?(dir)
27
+ pom_path = File.join(dir, "pom.xml")
28
+ if File.file?(pom_path)
29
+ log.info("pom.xml found in #{dir}/pom.xml")
25
30
  return File.read(pom_path)
31
+ end
26
32
  end
27
33
  end
28
- end
29
-
30
- # returns a pom embedded in a jar file
31
- def self.get_pom_from_jar(file)
32
- Zip::ZipFile.foreach(file) do |entry|
33
- if entry.name =~ /\/pom.xml$/
34
- $log.info("pom.xml found in #{file}##{entry.name}")
35
- return entry.get_input_stream.read
34
+
35
+ # returns a pom embedded in a jar file
36
+ def self.get_pom_from_jar(file)
37
+ begin
38
+ Zip::ZipFile.foreach(file) do |entry|
39
+ if entry.name =~ /\/pom.xml$/
40
+ log.info("pom.xml found in #{file}##{entry.name}")
41
+ return entry.get_input_stream.read
42
+ end
43
+ end
44
+ rescue Zip::ZipError
45
+ log.info "#{file} does not seem to be a valid jar archive, skipping"
46
+ rescue TypeError
47
+ log.info "#{file} seems to be a valid jar archive but is corrupt, skipping"
36
48
  end
49
+ return nil
37
50
  end
38
- return nil
39
- end
40
-
41
- # returns a pom from search.maven.org with a jar sha1 search
42
- def self.get_pom_from_sha1(file)
43
- sha1 = Digest::SHA1.hexdigest File.read(file)
44
- results = repository_search({:q => "1:\"#{sha1}\""}).select {|result| result["ec"].include?(".pom")}
45
- result = results.first
46
- if result != nil
47
- $log.info("pom.xml for #{file} found on search.maven.org for sha1 #{sha1} (#{result["g"]}:#{result["a"]}:#{result["v"]})")
48
- return repository_download(result)
51
+
52
+ # returns a pom from search.maven.org with a jar sha1 search
53
+ def self.get_pom_from_sha1(file)
54
+ begin
55
+ if File.file?(file)
56
+ sha1 = Digest::SHA1.hexdigest File.read(file)
57
+ results = repository_search({:q => "1:\"#{sha1}\""}).select {|result| result["ec"].include?(".pom")}
58
+ result = results.first
59
+ if result != nil
60
+ log.info("pom.xml for #{file} found on search.maven.org for sha1 #{sha1} (#{result["g"]}:#{result["a"]}:#{result["v"]})")
61
+ return repository_download(result)
62
+ end
63
+ end
64
+ return nil
65
+ rescue RestClient::ResourceNotFound
66
+ $stderr.puts "Got an error while looking for #{file}'s SHA1 in search.maven.org"
67
+ end
49
68
  end
50
- end
51
69
 
52
- # returns a pom from search.maven.org with a heuristic name search
53
- def self.get_pom_from_heuristic(file)
54
- filename = Pathname.new(file).basename.to_s
55
- if filename =~ /([^\/]*)\.jar$/
56
- my_artifact_id, my_version = VersionMatcher.split_version($1)
70
+ # returns a pom from search.maven.org with a heuristic name search
71
+ def self.get_pom_from_heuristic(filename)
72
+ begin
73
+ filename = Pathname.new(filename).basename.to_s.sub(/.jar$/, "")
74
+ my_artifact_id, my_version = VersionMatcher.split_version(filename)
57
75
 
58
- result = repository_search({:q => my_artifact_id}).first
59
- if result != nil
76
+ result = repository_search({:q => my_artifact_id}).first
77
+ if result != nil
60
78
  results = repository_search({:q => "g:\"#{result["g"]}\" AND a:\"#{result["a"]}\"", :core => "gav"})
61
79
  their_versions = results.map {|doc| doc["v"]}
62
80
  best_matched_version = if my_version != nil then VersionMatcher.best_match(my_version, their_versions) else their_versions.max end
63
81
  best_matched_result = (results.select{|result| result["v"] == best_matched_version}).first
64
-
65
- $log.warn("pom.xml for #{file} found on search.maven.org with heuristic search (#{best_matched_result["g"]}:#{best_matched_result["a"]}:#{best_matched_result["v"]})")
66
-
82
+
83
+ log.warn("pom.xml for #{filename} found on search.maven.org with heuristic search (#{best_matched_result["g"]}:#{best_matched_result["a"]}:#{best_matched_result["v"]})")
84
+
67
85
  return repository_download(best_matched_result)
86
+ end
87
+ rescue RestClient::ResourceNotFound
88
+ $stderr.puts "Got an error while looking for #{filename} in search.maven.org"
89
+ end
90
+ end
91
+
92
+ # returns a JSON result from search.maven.com
93
+ def self.repository_search(params)
94
+ response = RestClient.get "http://search.maven.org/solrsearch/select", {:params => params.merge({"rows" => "100", "wt" => "json"})}
95
+ json = JSON.parse(response.to_s)
96
+ return json["response"]["docs"]
97
+ end
98
+
99
+ # downloads a POM from a search.maven.com search result
100
+ def self.repository_download(result)
101
+ if result != nil
102
+ path = "#{result["g"].gsub(".", "/")}/#{result["a"]}/#{result["v"]}/#{result["a"]}-#{result["v"]}.pom"
103
+ return (RestClient.get "http://search.maven.org/remotecontent", {:params => {:filepath => path}}).to_s
68
104
  end
69
- end
70
- end
71
-
72
- # returns a JSON result from search.maven.com
73
- def self.repository_search(params)
74
- response = RestClient.get "http://search.maven.org/solrsearch/select", {:params => params.merge({"rows" => "100", "wt" => "json"})}
75
- json = JSON.parse(response.to_s)
76
- return json["response"]["docs"]
77
- end
78
-
79
- # downloads a POM from a search.maven.com search result
80
- def self.repository_download(result)
81
- if result != nil
82
- path = "#{result["g"].gsub(".", "/")}/#{result["a"]}/#{result["v"]}/#{result["a"]}-#{result["v"]}.pom"
83
- return (RestClient.get "http://search.maven.org/remotecontent", {:params => {:filepath => path}}).to_s
84
105
  end
85
106
  end
86
-
87
107
  end
88
-
@@ -2,80 +2,84 @@
2
2
 
3
3
  require "rest_client"
4
4
 
5
- # implements the get-source subcommand
6
- class SourceGetter
5
+ module Gjp
6
+ # implements the get-source subcommand
7
+ class SourceGetter
8
+ def self.log
9
+ Gjp.logger
10
+ end
7
11
 
8
- # downloads a project's source into a specified directory
9
- def self.get_source(address, pomfile, directory)
10
- $log.info("downloading: #{address} in #{directory}, pomfile: #{pomfile}")
11
-
12
- dummy, prefix, scm_address = address.split(/^([^:]+):(.*)$/)
13
- $log.info("prefix: #{prefix}, scm_address: #{scm_address}")
14
-
15
- get_source_from_scm(prefix, scm_address, pomfile, directory)
16
- end
12
+ # downloads a project's source into a specified directory
13
+ def self.get_source(address, pomfile, directory)
14
+ log.info("downloading: #{address} in #{directory}, pomfile: #{pomfile}")
15
+
16
+ dummy, prefix, scm_address = address.split(/^([^:]+):(.*)$/)
17
+ log.info("prefix: #{prefix}, scm_address: #{scm_address}")
18
+
19
+ get_source_from_scm(prefix, scm_address, pomfile, directory)
20
+ end
17
21
 
18
- # checks code out from an scm
19
- def self.get_source_from_scm(prefix, scm_address, pomfile, directory)
20
- pom = Pom.new(pomfile)
21
- dir = File.join(directory, "#{pom.group_id}:#{pom.artifact_id}:#{pom.version}")
22
- begin
23
- Dir::mkdir(dir)
24
- rescue Errno::EEXIST
25
- $log.warn("Source directory exists, leaving...")
26
- end
27
-
28
- if prefix == "git"
29
- get_source_from_git(scm_address, dir, pom.version)
30
- elsif prefix == "svn"
31
- get_source_from_svn(scm_address, dir, pom.version)
32
- end
33
- end
22
+ # checks code out from an scm
23
+ def self.get_source_from_scm(prefix, scm_address, pomfile, directory)
24
+ pom = Pom.new(pomfile)
25
+ dir = File.join(directory, "#{pom.group_id}:#{pom.artifact_id}:#{pom.version}")
26
+ begin
27
+ Dir::mkdir(dir)
28
+ rescue Errno::EEXIST
29
+ log.warn("Source directory exists, leaving...")
30
+ end
31
+
32
+ if prefix == "git"
33
+ get_source_from_git(scm_address, dir, pom.version)
34
+ elsif prefix == "svn"
35
+ get_source_from_svn(scm_address, dir, pom.version)
36
+ end
37
+ end
34
38
 
35
- # checks code out of git
36
- def self.get_source_from_git(scm_address, dir, version)
37
- `git clone #{scm_address} #{dir}`
38
-
39
- Dir.chdir(dir) do
40
- tags = `git tag`.split("\n")
41
-
42
- if tags.any?
43
- best_tag = get_best_tag(tags, version)
44
- $log.info("checking out tag: #{best_tag}")
39
+ # checks code out of git
40
+ def self.get_source_from_git(scm_address, dir, version)
41
+ `git clone #{scm_address} #{dir}`
42
+
43
+ Dir.chdir(dir) do
44
+ tags = `git tag`.split("\n")
45
+
46
+ if tags.any?
47
+ best_tag = get_best_tag(tags, version)
48
+ log.info("checking out tag: #{best_tag}")
45
49
 
46
- `git checkout #{best_tag}`
47
- end
48
- end
49
- end
50
+ `git checkout #{best_tag}`
51
+ end
52
+ end
53
+ end
50
54
 
51
- # checks code out of svn
52
- def self.get_source_from_svn(scm_address, dir, version)
53
- `svn checkout #{scm_address} #{dir}`
54
-
55
- Dir.chdir(dir) do
56
- tags = `svn ls "^/tags"`.split("\n")
57
-
58
- if tags.any?
59
- best_tag = get_best_tag(tags, version)
60
- $log.info("checking out tag: #{best_tag}")
55
+ # checks code out of svn
56
+ def self.get_source_from_svn(scm_address, dir, version)
57
+ `svn checkout #{scm_address} #{dir}`
58
+
59
+ Dir.chdir(dir) do
60
+ tags = `svn ls "^/tags"`.split("\n")
61
+
62
+ if tags.any?
63
+ best_tag = get_best_tag(tags, version)
64
+ log.info("checking out tag: #{best_tag}")
61
65
 
62
- `svn checkout #{scm_address}/tags/#{best_tag}`
63
- end
64
- end
65
- end
66
+ `svn checkout #{scm_address}/tags/#{best_tag}`
67
+ end
68
+ end
69
+ end
66
70
 
67
- # return the (heuristically) most similar tag to the specified version
68
- def self.get_best_tag(tags, version)
69
- versions_to_tags =Hash[
70
- *tags.map do |tag|
71
- [VersionMatcher.split_version(tag)[1], tag]
72
- end.flatten
73
- ]
74
-
75
- $log.info("found the following versions and tags: #{versions_to_tags}")
71
+ # return the (heuristically) most similar tag to the specified version
72
+ def self.get_best_tag(tags, version)
73
+ versions_to_tags =Hash[
74
+ *tags.map do |tag|
75
+ [VersionMatcher.split_version(tag)[1], tag]
76
+ end.flatten
77
+ ]
78
+
79
+ log.info("found the following versions and tags: #{versions_to_tags}")
76
80
 
77
- best_version = VersionMatcher.best_match(version, versions_to_tags.keys)
78
- versions_to_tags[best_version]
79
- end
81
+ best_version = VersionMatcher.best_match(version, versions_to_tags.keys)
82
+ versions_to_tags[best_version]
83
+ end
84
+ end
80
85
  end
81
-
@@ -4,45 +4,50 @@ require "rest_client"
4
4
  require "json"
5
5
  require "open-uri"
6
6
 
7
- # implements the get-source-address subcommand
8
- class SourceAddressGetter
7
+ module Gjp
8
+ # implements the get-source-address subcommand
9
+ class SourceAddressGetter
10
+ def self.log
11
+ Gjp.logger
12
+ end
9
13
 
10
- # returns the pom corresponding to a file or directory, if it can be found
11
- def self.get_source_address(file)
12
- $log.info("looking for source address for: #{file}")
13
- (get_source_address_from_pom(file) or get_source_address_from_github(file))
14
- end
14
+ # returns the pom corresponding to a file or directory, if it can be found
15
+ def self.get_source_address(file)
16
+ log.info("looking for source address for: #{file}")
17
+ (get_source_address_from_pom(file) or get_source_address_from_github(file))
18
+ end
15
19
 
16
- # returns an scm address in a pom file
17
- def self.get_source_address_from_pom(file)
18
- pom = Pom.new(file)
19
- result = pom.connection_address
20
+ # returns an scm address in a pom file
21
+ def self.get_source_address_from_pom(file)
22
+ pom = Pom.new(file)
23
+ result = pom.connection_address
20
24
 
21
- if result != nil
22
- $log.info("address found in pom")
23
- result
25
+ if result != nil
26
+ log.info("address found in pom")
27
+ result
28
+ end
24
29
  end
25
- end
26
-
27
- # returns an scm address looking for it on github
28
- def self.get_source_address_from_github(file)
29
- pom = Pom.new(file)
30
-
31
- result = (github_search(pom.artifact_id) or github_search(pom.artifact_id.split("-").first) or github_search(pom.group_id))
32
30
 
33
- if result != nil
34
- $log.info("address found on Github: #{result}")
35
- result
31
+ # returns an scm address looking for it on github
32
+ def self.get_source_address_from_github(file)
33
+ pom = Pom.new(file)
34
+
35
+ result = (github_search(pom.artifact_id) or github_search(pom.artifact_id.split("-").first) or github_search(pom.group_id))
36
+
37
+ if result != nil
38
+ log.info("address found on Github: #{result}")
39
+ result
40
+ end
36
41
  end
37
- end
38
-
39
- # returns a Giuthub repo address based on the keyword
40
- def self.github_search(keyword)
41
- if keyword != "" and keyword != nil
42
- response = RestClient.get "https://api.github.com/legacy/repos/search/" + CGI::escape(keyword), :user_agent => "gjp/" + Gjp::VERSION, :language => "java", :sort => "forks"
43
- json = JSON.parse(response.to_s)
44
42
 
45
- (json["repositories"].map {|repository| "git:" + repository["url"]}).first
43
+ # returns a Giuthub repo address based on the keyword
44
+ def self.github_search(keyword)
45
+ if keyword != "" and keyword != nil
46
+ response = RestClient.get "https://api.github.com/legacy/repos/search/" + CGI::escape(keyword), :user_agent => "gjp/" + Gjp::VERSION, :language => "java", :sort => "forks"
47
+ json = JSON.parse(response.to_s)
48
+
49
+ (json["repositories"].map {|repository| "git:" + repository["url"]}).first
50
+ end
46
51
  end
47
52
  end
48
53
  end
@@ -2,23 +2,16 @@
2
2
 
3
3
  require "logger"
4
4
 
5
- def init_logger(level = nil)
6
- $log = Logger.new(STDERR)
7
- $log.level = if level == nil
8
- if very_very_verbose?
9
- Logger::DEBUG
10
- elsif very_verbose?
11
- Logger::INFO
12
- elsif verbose?
13
- Logger::WARN
14
- else
15
- Logger::ERROR
16
- end
17
- else
18
- level
5
+ module Gjp
6
+ def self.logger=(logger)
7
+ @logger = logger
19
8
  end
20
-
21
- $log.formatter = proc do |severity, datetime, progname, msg|
22
- "#{severity.chars.first}: #{msg}\n"
9
+
10
+ def self.logger
11
+ @logger ||= Logger.new('/dev/null')
12
+ end
13
+
14
+ def logger
15
+ Gjp.logger
23
16
  end
24
17
  end
@@ -1,30 +1,34 @@
1
- # encoding: UTF-8
1
+ # encoding: UTF-8
2
2
 
3
3
  require "nokogiri"
4
+ require "open-uri"
4
5
 
5
- # encapsulates a pom.xml file
6
- class Pom
7
- def initialize(filename)
8
- @doc = Nokogiri::XML(File.read(filename))
9
- @doc.remove_namespaces!
10
- end
11
-
12
- def connection_address
13
- connection_nodes = @doc.xpath("//scm/connection/text()")
14
- if connection_nodes.any?
15
- connection_nodes.first.to_s.sub(/^scm:/, "")
6
+ module Gjp
7
+ # encapsulates a pom.xml file
8
+ class Pom
9
+ def initialize(filename)
10
+ @doc = Nokogiri::XML(open(filename).read)
11
+ @doc.remove_namespaces!
12
+ end
13
+
14
+ def connection_address
15
+ connection_nodes = @doc.xpath("//scm/connection/text()")
16
+ if connection_nodes.any?
17
+ connection_nodes.first.to_s.sub(/^scm:/, "")
18
+ end
19
+ end
20
+
21
+ def group_id
22
+ @doc.xpath("project/groupId/text()").to_s
23
+ end
24
+
25
+ def artifact_id
26
+ @doc.xpath("project/artifactId/text()").to_s
27
+ end
28
+
29
+ def version
30
+ @doc.xpath("project/version/text()").to_s
16
31
  end
17
- end
18
-
19
- def group_id
20
- @doc.xpath("project/groupId/text()").to_s
21
- end
22
-
23
- def artifact_id
24
- @doc.xpath("project/artifactId/text()").to_s
25
- end
26
-
27
- def version
28
- @doc.xpath("project/version/text()").to_s
29
32
  end
30
33
  end
34
+
@@ -1,5 +1,5 @@
1
1
  # encoding: UTF-8
2
2
 
3
3
  module Gjp
4
- VERSION = "0.6.0"
4
+ VERSION = "0.7.0"
5
5
  end
@@ -2,93 +2,92 @@
2
2
 
3
3
  require "text"
4
4
 
5
- # heuristically matches version strings
6
- class VersionMatcher
7
-
8
- # heuristically extracts a version number from a full name
9
- # assumes that all leading non-numeric chars are not related the version
10
- # returns a version string
11
- def self.extract_version(full_name)
12
- full_name.sub /^[^0-9]+/, ""
13
- end
5
+ module Gjp
6
+ # heuristically matches version strings
7
+ class VersionMatcher
8
+ def self.log
9
+ Gjp.logger
10
+ end
14
11
 
15
- # heuristically splits a full name into an artifact name and version string
16
- # assumes that version strings begin with a numeric character and are separated
17
- # by a ., -, _, ~ or space
18
- # returns a [name, version] pair
19
- def self.split_version(full_name)
20
- matches = full_name.match(/(.*?)(?:[\.\-\_ ~,]?([0-9].*))?$/)
21
- if matches != nil and matches.length > 1
22
- [matches[1], matches[2]]
23
- else
24
- [full_string, nil]
25
- end
26
- end
12
+ # heuristically splits a full name into an artifact name and version string
13
+ # assumes that version strings begin with a numeric character and are separated
14
+ # by a ., -, _, ~ or space
15
+ # returns a [name, version] pair
16
+ def self.split_version(full_name)
17
+ matches = full_name.match(/(.*?)(?:[\.\-\_ ~,]?([0-9].*))?$/)
18
+ if matches != nil and matches.length > 1
19
+ [matches[1], matches[2]]
20
+ else
21
+ [full_string, nil]
22
+ end
23
+ end
27
24
 
28
- # returns the "best match" between a version number and a set of available version numbers
29
- # using a heuristic criterion. Idea:
30
- # - split the version number in chunks divided by ., - etc.
31
- # - every chunk with same index is "compared", differences make up a score
32
- # - "comparison" is a subtraction if the chunk is an integer, a string distance measure otherwise
33
- # - score weighs differently on chunk index (first chunks are most important)
34
- # - lowest score wins
35
- def self.best_match(my_version, their_versions)
36
- $log.debug("version comparison: #{my_version} vs #{their_versions.join(', ')}")
37
-
38
- my_chunks = my_version.split /[\.\-\_ ~,]/
39
- their_chunks_hash = Hash[
40
- their_versions.map do |their_version|
41
- their_chunks_for_version = their_version.split /[\.\-\_ ~,]/
42
- their_chunks_for_version += [nil]*[my_chunks.length - their_chunks_for_version.length, 0].max
43
- [their_version, their_chunks_for_version]
44
- end
45
- ]
25
+ # returns the "best match" between a version number and a set of available version numbers
26
+ # using a heuristic criterion. Idea:
27
+ # - split the version number in chunks divided by ., - etc.
28
+ # - every chunk with same index is "compared", differences make up a score
29
+ # - "comparison" is a subtraction if the chunk is an integer, a string distance measure otherwise
30
+ # - score weighs differently on chunk index (first chunks are most important)
31
+ # - lowest score wins
32
+ def self.best_match(my_version, their_versions)
33
+ log.debug("version comparison: #{my_version} vs #{their_versions.join(', ')}")
46
34
 
47
- max_chunks_length = ([my_chunks.length] + their_chunks_hash.values.map {|chunk| chunk.length}).max
48
-
49
- scoreboard = []
50
- their_versions.each do |their_version|
51
- their_chunks = their_chunks_hash[their_version]
52
- score = 0
53
- their_chunks.each_with_index do |their_chunk, i|
54
- score_multiplier = 100**(max_chunks_length -i -1)
55
- my_chunk = my_chunks[i]
56
- score += chunk_distance(my_chunk, their_chunk) * score_multiplier
35
+ my_chunks = my_version.split /[\.\-\_ ~,]/
36
+ their_chunks_hash = Hash[
37
+ their_versions.map do |their_version|
38
+ their_chunks_for_version = their_version.split /[\.\-\_ ~,]/
39
+ their_chunks_for_version += [nil]*[my_chunks.length - their_chunks_for_version.length, 0].max
40
+ [their_version, their_chunks_for_version]
41
+ end
42
+ ]
43
+
44
+ max_chunks_length = ([my_chunks.length] + their_chunks_hash.values.map {|chunk| chunk.length}).max
45
+
46
+ scoreboard = []
47
+ their_versions.each do |their_version|
48
+ their_chunks = their_chunks_hash[their_version]
49
+ score = 0
50
+ their_chunks.each_with_index do |their_chunk, i|
51
+ score_multiplier = 100**(max_chunks_length -i -1)
52
+ my_chunk = my_chunks[i]
53
+ score += chunk_distance(my_chunk, their_chunk) * score_multiplier
54
+ end
55
+ scoreboard << {:version => their_version, :score => score}
57
56
  end
58
- scoreboard << {:version => their_version, :score => score}
59
- end
60
-
61
- scoreboard = scoreboard.sort_by {|element| element[:score]}
57
+
58
+ scoreboard = scoreboard.sort_by {|element| element[:score]}
62
59
 
63
- $log.debug("scoreboard: ")
64
- scoreboard.each_with_index do |element, i|
65
- $log.debug(" #{i+1}. #{element[:version]} (score: #{element[:score]})")
60
+ log.debug("scoreboard: ")
61
+ scoreboard.each_with_index do |element, i|
62
+ log.debug(" #{i+1}. #{element[:version]} (score: #{element[:score]})")
63
+ end
64
+
65
+ winner = scoreboard.first
66
+
67
+ if winner != nil
68
+ return winner[:version]
69
+ end
66
70
  end
67
71
 
68
- winner = scoreboard.first
69
-
70
- if winner != nil
71
- return winner[:version]
72
- end
73
- end
74
-
75
- # returns a score representing the distance between two version chunks
76
- # for integers, the score is the difference between their values
77
- # for strings, the score is the Levenshtein distance
78
- # in any case score is normalized between 0 (identical) and 99 (very different/uncomparable)
79
- def self.chunk_distance(my_chunk, their_chunk)
80
- if my_chunk == nil
81
- my_chunk = "0"
82
- end
83
- if their_chunk == nil
84
- their_chunk = "0"
85
- end
86
- if my_chunk.is_i? and their_chunk.is_i?
87
- return [(my_chunk.to_i - their_chunk.to_i).abs, 99].min
88
- else
89
- return [Text::Levenshtein.distance(my_chunk.upcase, their_chunk.upcase), 99].min
72
+ # returns a score representing the distance between two version chunks
73
+ # for integers, the score is the difference between their values
74
+ # for strings, the score is the Levenshtein distance
75
+ # in any case score is normalized between 0 (identical) and 99 (very different/uncomparable)
76
+ def self.chunk_distance(my_chunk, their_chunk)
77
+ if my_chunk == nil
78
+ my_chunk = "0"
79
+ end
80
+ if their_chunk == nil
81
+ their_chunk = "0"
82
+ end
83
+ if my_chunk.is_i? and their_chunk.is_i?
84
+ return [(my_chunk.to_i - their_chunk.to_i).abs, 99].min
85
+ else
86
+ return [Text::Levenshtein.distance(my_chunk.upcase, their_chunk.upcase), 99].min
87
+ end
90
88
  end
91
89
  end
90
+
92
91
  end
93
92
 
94
93
  class String
@@ -96,4 +95,3 @@ class String
96
95
  !!(self =~ /^[0-9]+$/)
97
96
  end
98
97
  end
99
-
@@ -2,33 +2,33 @@
2
2
 
3
3
  require 'spec_helper'
4
4
 
5
- describe PomGetter do
5
+ describe Gjp::PomGetter do
6
6
  describe ".get_pom" do
7
7
  it "gets the pom from a directory" do
8
8
  dir_path = File.join("spec", "data", "commons-logging")
9
9
  pom_path = File.join(dir_path, "pom.xml")
10
- PomGetter.get_pom(dir_path).should eq(File.read(pom_path))
10
+ Gjp::PomGetter.get_pom(dir_path).should eq(File.read(pom_path))
11
11
  end
12
12
 
13
13
  it "gets the pom from a jar" do
14
14
  dir_path = File.join("spec", "data", "commons-logging")
15
15
  pom_path = File.join(dir_path, "pom.xml")
16
16
  jar_path = File.join(dir_path, "commons-logging-1.1.1.jar")
17
- PomGetter.get_pom(jar_path).should eq(File.read(pom_path))
17
+ Gjp::PomGetter.get_pom(jar_path).should eq(File.read(pom_path))
18
18
  end
19
19
 
20
20
  it "gets the pom from sha1" do
21
21
  dir_path = File.join("spec", "data", "antlr")
22
22
  pom_path = File.join(dir_path, "pom.xml")
23
23
  jar_path = File.join(dir_path, "antlr-2.7.2.jar")
24
- PomGetter.get_pom(jar_path).should eq(File.read(pom_path))
24
+ Gjp::PomGetter.get_pom(jar_path).should eq(File.read(pom_path))
25
25
  end
26
26
 
27
27
  it "gets the pom from a heuristic" do
28
28
  dir_path = File.join("spec", "data", "nailgun")
29
29
  pom_path = File.join(dir_path, "pom.xml")
30
30
  jar_path = File.join(dir_path, "nailgun-0.7.1.jar")
31
- PomGetter.get_pom(jar_path).should eq(File.read(pom_path))
31
+ Gjp::PomGetter.get_pom(jar_path).should eq(File.read(pom_path))
32
32
  end
33
33
  end
34
34
  end
@@ -2,16 +2,16 @@
2
2
 
3
3
  require 'spec_helper'
4
4
 
5
- describe SourceAddressGetter do
5
+ describe Gjp::SourceAddressGetter do
6
6
  describe ".get_source_address" do
7
7
  it "gets the source address from a pom file" do
8
8
  pom_path = File.join("spec", "data", "commons-logging", "pom.xml")
9
- SourceAddressGetter.get_source_address(pom_path).should eq "svn:http://svn.apache.org/repos/asf/commons/proper/logging/tags/commons-logging-1.1.1"
9
+ Gjp::SourceAddressGetter.get_source_address(pom_path).should eq "svn:http://svn.apache.org/repos/asf/commons/proper/logging/tags/commons-logging-1.1.1"
10
10
  end
11
11
 
12
12
  it "gets the source address from Github" do
13
13
  pom_path = File.join("spec", "data", "antlr", "pom.xml")
14
- SourceAddressGetter.get_source_address(pom_path).should eq "git:https://github.com/antlr/antlr4"
14
+ Gjp::SourceAddressGetter.get_source_address(pom_path).should eq "git:https://github.com/antlr/antlr4"
15
15
  end
16
16
  end
17
17
  end
@@ -3,7 +3,7 @@
3
3
  require "spec_helper"
4
4
  require "fileutils"
5
5
 
6
- describe SourceGetter do
6
+ describe Gjp::SourceGetter do
7
7
  describe ".get_source_from_git" do
8
8
  it "gets the sources from a git repo" do
9
9
  dir_path = File.join("spec", "data", "nailgun")
@@ -13,7 +13,7 @@ describe SourceGetter do
13
13
 
14
14
  FileUtils.rm_rf(repo_path)
15
15
 
16
- SourceGetter.get_source("git:git@github.com:martylamb/nailgun.git", pom_path, dir_path)
16
+ Gjp::SourceGetter.get_source("git:git@github.com:martylamb/nailgun.git", pom_path, dir_path)
17
17
 
18
18
  File.open(file_path).readline.should eq "nailgun\n"
19
19
  end
@@ -28,7 +28,7 @@ describe SourceGetter do
28
28
 
29
29
  FileUtils.rm_rf(repo_path)
30
30
 
31
- SourceGetter.get_source("svn:http://svn.apache.org/repos/asf/struts/struts2/tags/STRUTS_2_3_14/apps", pom_path, dir_path)
31
+ Gjp::SourceGetter.get_source("svn:http://svn.apache.org/repos/asf/struts/struts2/tags/STRUTS_2_3_14/apps", pom_path, dir_path)
32
32
 
33
33
  File.open(file_path).readline.should eq "README.txt - showcase\n"
34
34
  end
@@ -2,30 +2,38 @@
2
2
 
3
3
  require 'spec_helper'
4
4
 
5
- describe Pom do
6
- let(:pom) { Pom.new(File.join("spec", "data", "commons-logging", "pom.xml")) }
7
-
8
- describe "#connection_address" do
9
- it "reads the SCM connection address" do
10
- pom.connection_address.should eq "svn:http://svn.apache.org/repos/asf/commons/proper/logging/tags/commons-logging-1.1.1"
5
+ describe Gjp::Pom do
6
+ [ File.join("spec", "data", "commons-logging", "pom.xml"),
7
+ 'http://search.maven.org/remotecontent?filepath=commons-logging/commons-logging/1.1.1/commons-logging-1.1.1.pom'].each do |loc|
8
+
9
+ let(:pom) { Gjp::Pom.new(loc) }
10
+
11
+ describe "#connection_address" do
12
+ it "reads the SCM connection address" do
13
+ pom.connection_address.should eq "svn:http://svn.apache.org/repos/asf/commons/proper/logging/tags/commons-logging-1.1.1"
14
+ end
15
+
16
+ it "reads the SCM connection address from a remote repository" do
17
+ pom.connection_address.should eq "svn:http://svn.apache.org/repos/asf/commons/proper/logging/tags/commons-logging-1.1.1"
18
+ end
11
19
  end
12
- end
13
-
14
- describe "#group_id" do
15
- it "reads the group id" do
16
- pom.group_id.should eq "commons-logging"
20
+
21
+ describe "#group_id" do
22
+ it "reads the group id" do
23
+ pom.group_id.should eq "commons-logging"
24
+ end
17
25
  end
18
- end
19
26
 
20
- describe "#artifact_id" do
21
- it "reads the artifact id" do
22
- pom.artifact_id.should eq "commons-logging"
27
+ describe "#artifact_id" do
28
+ it "reads the artifact id" do
29
+ pom.artifact_id.should eq "commons-logging"
30
+ end
23
31
  end
24
- end
25
-
26
- describe "#version" do
27
- it "reads the version" do
28
- pom.version.should eq "1.1.1"
32
+
33
+ describe "#version" do
34
+ it "reads the version" do
35
+ pom.version.should eq "1.1.1"
36
+ end
29
37
  end
30
38
  end
31
39
  end
@@ -2,63 +2,57 @@
2
2
 
3
3
  require 'spec_helper'
4
4
 
5
- describe VersionMatcher do
5
+ describe Gjp::VersionMatcher do
6
6
 
7
7
  it "splits full names into names and version numbers" do
8
- VersionMatcher.split_version("moio-3.2beta1").should eq(["moio", "3.2beta1"])
9
- VersionMatcher.split_version("3.2beta1").should eq(["3", "2beta1"])
10
- VersionMatcher.split_version("v3.2beta1").should eq(["v", "3.2beta1"])
11
- end
12
-
13
- it "extracts version numbers" do
14
- VersionMatcher.extract_version("moio-3.2beta1").should eq("3.2beta1")
15
- VersionMatcher.extract_version("3.2beta1").should eq("3.2beta1")
16
- VersionMatcher.extract_version("v3.2beta1").should eq("3.2beta1")
8
+ Gjp::VersionMatcher.split_version("moio-3.2beta1").should eq(["moio", "3.2beta1"])
9
+ Gjp::VersionMatcher.split_version("3.2beta1").should eq(["", "3.2beta1"])
10
+ Gjp::VersionMatcher.split_version("v3.2beta1").should eq(["v", "3.2beta1"])
17
11
  end
18
12
 
19
13
  it "computes chunk distances" do
20
- VersionMatcher.chunk_distance(nil, "1").should eq(1)
21
- VersionMatcher.chunk_distance("alpha", nil).should eq(5)
14
+ Gjp::VersionMatcher.chunk_distance(nil, "1").should eq(1)
15
+ Gjp::VersionMatcher.chunk_distance("alpha", nil).should eq(5)
22
16
 
23
- VersionMatcher.chunk_distance("1", "1").should eq(0)
24
- VersionMatcher.chunk_distance("1", "9").should eq(8)
25
- VersionMatcher.chunk_distance("1", "999").should eq(99)
17
+ Gjp::VersionMatcher.chunk_distance("1", "1").should eq(0)
18
+ Gjp::VersionMatcher.chunk_distance("1", "9").should eq(8)
19
+ Gjp::VersionMatcher.chunk_distance("1", "999").should eq(99)
26
20
 
27
- VersionMatcher.chunk_distance("snap", "SNAP").should eq(0)
28
- VersionMatcher.chunk_distance("snap", "snippete").should eq(5)
29
- VersionMatcher.chunk_distance("snap", "l"*999).should eq(99)
21
+ Gjp::VersionMatcher.chunk_distance("snap", "SNAP").should eq(0)
22
+ Gjp::VersionMatcher.chunk_distance("snap", "snippete").should eq(5)
23
+ Gjp::VersionMatcher.chunk_distance("snap", "l"*999).should eq(99)
30
24
 
31
- VersionMatcher.chunk_distance("1", "SNAP").should eq(4)
25
+ Gjp::VersionMatcher.chunk_distance("1", "SNAP").should eq(4)
32
26
 
33
- VersionMatcher.chunk_distance("0", "10").should eq(10)
34
- VersionMatcher.chunk_distance("0", "9").should eq(9)
27
+ Gjp::VersionMatcher.chunk_distance("0", "10").should eq(10)
28
+ Gjp::VersionMatcher.chunk_distance("0", "9").should eq(9)
35
29
  end
36
30
 
37
31
  it "finds the best match" do
38
32
  my_version = "1.0"
39
33
  available_versions = ["1.0", "1", "2.0", "1.0.1", "4.5.6.7.8"]
40
- VersionMatcher.best_match(my_version, available_versions).should eq("1.0")
34
+ Gjp::VersionMatcher.best_match(my_version, available_versions).should eq("1.0")
41
35
 
42
36
  available_versions = ["3.0", "2.0", "1.0.1"]
43
- VersionMatcher.best_match(my_version, available_versions).should eq("1.0.1")
37
+ Gjp::VersionMatcher.best_match(my_version, available_versions).should eq("1.0.1")
44
38
 
45
39
  available_versions = ["1.snap", "2.0", "4.0.1"]
46
- VersionMatcher.best_match(my_version, available_versions).should eq("1.snap")
40
+ Gjp::VersionMatcher.best_match(my_version, available_versions).should eq("1.snap")
47
41
 
48
42
  available_versions = ["1.10", "1.9", "2.0", "3.0.1"]
49
- VersionMatcher.best_match(my_version, available_versions).should eq("1.9")
43
+ Gjp::VersionMatcher.best_match(my_version, available_versions).should eq("1.9")
50
44
 
51
45
  my_version = "1.snap"
52
46
  available_versions = ["1.snap", "1"]
53
- VersionMatcher.best_match(my_version, available_versions).should eq("1.snap")
47
+ Gjp::VersionMatcher.best_match(my_version, available_versions).should eq("1.snap")
54
48
 
55
49
  my_version = "1.very-very_very_longish"
56
50
  available_versions = ["1.snap", "1"]
57
- VersionMatcher.best_match(my_version, available_versions).should eq("1.snap")
51
+ Gjp::VersionMatcher.best_match(my_version, available_versions).should eq("1.snap")
58
52
 
59
53
  my_version = "1.snap"
60
54
  available_versions = []
61
- VersionMatcher.best_match(my_version, available_versions).should be_nil
55
+ Gjp::VersionMatcher.best_match(my_version, available_versions).should be_nil
62
56
  end
63
57
  end
64
58
 
@@ -3,4 +3,8 @@
3
3
  require "gjp"
4
4
  require "logger"
5
5
 
6
- init_logger(Logger::INFO)
6
+ Gjp.logger = ::Logger.new(STDERR)
7
+ Gjp.logger.level = ::Logger::INFO
8
+ Gjp.logger.formatter = proc do |severity, datetime, progname, msg|
9
+ "#{severity.chars.first}: #{msg}\n"
10
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: gjp
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.0
4
+ version: 0.7.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-06-03 00:00:00.000000000 Z
12
+ date: 2013-06-12 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rake