gjp 0.7.0 → 0.8.0
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +28 -0
- data/README.md +2 -1
- data/lib/gjp.rb +2 -0
- data/lib/gjp/cli.rb +39 -29
- data/lib/gjp/get_parent_pom.rb +25 -0
- data/lib/gjp/get_pom.rb +11 -21
- data/lib/gjp/get_source.rb +1 -1
- data/lib/gjp/maven_website.rb +61 -0
- data/lib/gjp/pom.rb +12 -0
- data/lib/gjp/version.rb +1 -1
- data/lib/gjp/version_matcher.rb +5 -1
- data/spec/data/commons-logging/parent_pom.xml +420 -0
- data/spec/lib/get_parent_pom_spec.rb +14 -0
- data/spec/lib/maven_website_spec.rb +57 -0
- data/spec/lib/pom_spec.rb +18 -0
- data/spec/spec_helper.rb +1 -1
- metadata +11 -2
data/LICENSE
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
Copyright (c) 2013 Silvio Moioli and Duncan Mac-Vicar Prett.
|
2
|
+
All rights reserved.
|
3
|
+
|
4
|
+
Redistribution and use in source and binary forms, with or without
|
5
|
+
modification, are permitted provided that the following conditions are
|
6
|
+
met:
|
7
|
+
|
8
|
+
* Redistributions of source code must retain the above copyright
|
9
|
+
notice, this list of conditions and the following disclaimer.
|
10
|
+
* Redistributions in binary form must reproduce the above
|
11
|
+
copyright notice, this list of conditions and the following disclaimer
|
12
|
+
in the documentation and/or other materials provided with the
|
13
|
+
distribution.
|
14
|
+
* Neither the name of Google Inc. nor the names of its
|
15
|
+
contributors may be used to endorse or promote products derived from
|
16
|
+
this software without specific prior written permission.
|
17
|
+
|
18
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
19
|
+
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
20
|
+
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
21
|
+
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
22
|
+
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
23
|
+
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
24
|
+
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
25
|
+
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
26
|
+
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
27
|
+
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
28
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
data/README.md
CHANGED
@@ -16,7 +16,8 @@ Easiest install is via RubyGems:
|
|
16
16
|
|
17
17
|
Currently available tools:
|
18
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-
|
19
|
+
* `gjp get-parent-pom POM` will attempt to download a pom's parent from search.maven.org, where `POM` is a filename or URI;
|
20
|
+
* `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. `POM` can either be a filename or a URI;
|
20
21
|
* `gjp get-source POM ADDRESS` downloads the source of a pom.xml's project from its SCM at ADDRESS;
|
21
22
|
|
22
23
|
## Source
|
data/lib/gjp.rb
CHANGED
data/lib/gjp/cli.rb
CHANGED
@@ -2,53 +2,63 @@
|
|
2
2
|
require "gjp/logger"
|
3
3
|
require "clamp"
|
4
4
|
|
5
|
-
# Initialize global logger
|
5
|
+
# Initialize global logger for CLI
|
6
6
|
Gjp.logger = ::Logger.new(STDERR)
|
7
7
|
Gjp.logger.datetime_format = "%Y-%m-%d %H:%M "
|
8
|
-
Gjp.logger.level = ::Logger::
|
8
|
+
Gjp.logger.level = ::Logger::ERROR
|
9
9
|
Gjp.logger.formatter = proc do |severity, datetime, progname, msg|
|
10
10
|
"#{severity.chars.first}: #{msg}\n"
|
11
11
|
end
|
12
12
|
|
13
13
|
module Gjp
|
14
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
15
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
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
|
16
|
+
# Common logging options
|
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"
|
32
20
|
|
33
|
-
|
34
|
-
|
35
|
-
|
21
|
+
def very_very_verbose=(flag)
|
22
|
+
configure_log_level(verbose?, very_verbose?, flag)
|
23
|
+
end
|
24
|
+
|
25
|
+
def very_verbose=(flag)
|
26
|
+
configure_log_level(verbose?, flag, very_very_verbose?)
|
27
|
+
end
|
28
|
+
|
29
|
+
def verbose=(flag)
|
30
|
+
configure_log_level(flag, very_verbose?, very_very_verbose?)
|
31
|
+
end
|
36
32
|
|
37
|
-
|
38
|
-
|
33
|
+
def configure_log_level(v, vv, vvv)
|
34
|
+
if vvv
|
35
|
+
Gjp.logger.level = Logger::DEBUG
|
36
|
+
elsif vv
|
37
|
+
Gjp.logger.level = Logger::INFO
|
38
|
+
elsif v
|
39
|
+
Gjp.logger.level = Logger::WARN
|
40
|
+
else
|
41
|
+
Gjp.logger.level = Logger::ERROR
|
39
42
|
end
|
43
|
+
end
|
40
44
|
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
+
# Subcommands
|
46
|
+
subcommand "get-pom", "Retrieves a pom corresponding to a filename" do
|
47
|
+
parameter "NAME", "a jar file path, a project directory path or a non-existing filename in the `project-version` form"
|
45
48
|
def execute
|
46
49
|
puts Gjp::PomGetter.get_pom(name)
|
47
50
|
end
|
48
51
|
end
|
52
|
+
|
53
|
+
subcommand "get-parent-pom", "Retrieves a pom that is the parent of an existing pom" do
|
54
|
+
parameter "POM", "a pom file path or URI"
|
55
|
+
def execute
|
56
|
+
puts Gjp::ParentPomGetter.get_parent_pom(pom)
|
57
|
+
end
|
58
|
+
end
|
49
59
|
|
50
60
|
subcommand "get-source-address", "Retrieves a project's SCM Internet address" do
|
51
|
-
parameter "POM", "
|
61
|
+
parameter "POM", "a pom file path or URI"
|
52
62
|
|
53
63
|
def execute
|
54
64
|
puts Gjp::SourceAddressGetter.get_source_address(pom)
|
@@ -57,7 +67,7 @@ module Gjp
|
|
57
67
|
|
58
68
|
subcommand "get-source", "Retrieves a project's source code directory" do
|
59
69
|
parameter "ADDRESS", "project's SCM Internet address"
|
60
|
-
parameter "POM", "project's pom file path"
|
70
|
+
parameter "POM", "project's pom file path or URI"
|
61
71
|
parameter "[DIRECTORY]", "directory in which to save the source code", :default => "."
|
62
72
|
|
63
73
|
def execute
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
require "pathname"
|
4
|
+
|
5
|
+
module Gjp
|
6
|
+
# implements the get-parent-pom subcommand
|
7
|
+
class ParentPomGetter
|
8
|
+
|
9
|
+
def self.log
|
10
|
+
Gjp.logger
|
11
|
+
end
|
12
|
+
|
13
|
+
# returns the pom's parent, if any
|
14
|
+
def self.get_parent_pom(filename)
|
15
|
+
begin
|
16
|
+
pom = Pom.new(filename)
|
17
|
+
site = MavenWebsite.new
|
18
|
+
|
19
|
+
site.download_pom(pom.parent_group_id, pom.parent_artifact_id, pom.parent_version)
|
20
|
+
rescue RestClient::ResourceNotFound
|
21
|
+
$stderr.puts "Could not find a parent for this pom!"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
data/lib/gjp/get_pom.rb
CHANGED
@@ -53,12 +53,14 @@ module Gjp
|
|
53
53
|
def self.get_pom_from_sha1(file)
|
54
54
|
begin
|
55
55
|
if File.file?(file)
|
56
|
+
site = MavenWebsite.new
|
56
57
|
sha1 = Digest::SHA1.hexdigest File.read(file)
|
57
|
-
results =
|
58
|
+
results = site.search_by_sha1(sha1).select {|result| result["ec"].include?(".pom")}
|
58
59
|
result = results.first
|
59
60
|
if result != nil
|
60
61
|
log.info("pom.xml for #{file} found on search.maven.org for sha1 #{sha1} (#{result["g"]}:#{result["a"]}:#{result["v"]})")
|
61
|
-
|
62
|
+
group_id, artifact_id, version = site.get_maven_id_from result
|
63
|
+
return site.download_pom(group_id, artifact_id, version)
|
62
64
|
end
|
63
65
|
end
|
64
66
|
return nil
|
@@ -70,38 +72,26 @@ module Gjp
|
|
70
72
|
# returns a pom from search.maven.org with a heuristic name search
|
71
73
|
def self.get_pom_from_heuristic(filename)
|
72
74
|
begin
|
75
|
+
site = MavenWebsite.new
|
73
76
|
filename = Pathname.new(filename).basename.to_s.sub(/.jar$/, "")
|
74
77
|
my_artifact_id, my_version = VersionMatcher.split_version(filename)
|
75
78
|
|
76
|
-
result =
|
79
|
+
result = site.search_by_name(my_artifact_id).first
|
77
80
|
if result != nil
|
78
|
-
|
81
|
+
group_id, artifact_id, version = site.get_maven_id_from result
|
82
|
+
results = site.search_by_group_id_and_artifact_id(group_id, artifact_id)
|
79
83
|
their_versions = results.map {|doc| doc["v"]}
|
80
84
|
best_matched_version = if my_version != nil then VersionMatcher.best_match(my_version, their_versions) else their_versions.max end
|
81
85
|
best_matched_result = (results.select{|result| result["v"] == best_matched_version}).first
|
82
86
|
|
83
|
-
|
87
|
+
group_id, artifact_id, version = site.get_maven_id_from(best_matched_result)
|
88
|
+
log.warn("pom.xml for #{filename} found on search.maven.org with heuristic search (#{group_id}:#{artifact_id}:#{version})")
|
84
89
|
|
85
|
-
return
|
90
|
+
return site.download_pom(group_id, artifact_id, version)
|
86
91
|
end
|
87
92
|
rescue RestClient::ResourceNotFound
|
88
93
|
$stderr.puts "Got an error while looking for #{filename} in search.maven.org"
|
89
94
|
end
|
90
95
|
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
|
104
|
-
end
|
105
|
-
end
|
106
96
|
end
|
107
97
|
end
|
data/lib/gjp/get_source.rb
CHANGED
@@ -0,0 +1,61 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
require "text"
|
4
|
+
|
5
|
+
module Gjp
|
6
|
+
# Facade to search.maven.org
|
7
|
+
class MavenWebsite
|
8
|
+
def log
|
9
|
+
Gjp.logger
|
10
|
+
end
|
11
|
+
|
12
|
+
# returns a search result object from search.maven.com
|
13
|
+
# searching by a jar sha1 hash
|
14
|
+
# see output format at http://search.maven.org/#api
|
15
|
+
def search_by_sha1(sha1)
|
16
|
+
return search({:q => "1:\"#{sha1}\""})
|
17
|
+
end
|
18
|
+
|
19
|
+
# returns a search result object from search.maven.com
|
20
|
+
# searching by keyword (name)
|
21
|
+
# see output format at http://search.maven.org/#api
|
22
|
+
def search_by_name(name)
|
23
|
+
return search({:q => name})
|
24
|
+
end
|
25
|
+
|
26
|
+
# returns a search result object from search.maven.com
|
27
|
+
# searching by Maven's group id and artifact id
|
28
|
+
# see output format at http://search.maven.org/#api
|
29
|
+
def search_by_group_id_and_artifact_id(group_id, artifact_id)
|
30
|
+
return search({:q => "g:\"#{group_id}\" AND a:\"#{artifact_id}\"", :core => "gav"})
|
31
|
+
end
|
32
|
+
|
33
|
+
# returns a search result object from search.maven.com
|
34
|
+
# searching by Maven's id (group id, artifact id and version)
|
35
|
+
# see output format at http://search.maven.org/#api
|
36
|
+
def search_by_maven_id(group_id, artifact_id, version)
|
37
|
+
return search({:q => "g:\"#{group_id}\" AND a:\"#{artifact_id}\" AND v:\"#{version}\""})
|
38
|
+
end
|
39
|
+
|
40
|
+
# returns a search result object from search.maven.com
|
41
|
+
# see input and output format at http://search.maven.org/#api
|
42
|
+
def search(params)
|
43
|
+
response = RestClient.get "http://search.maven.org/solrsearch/select", {:params => params.merge({"rows" => "100", "wt" => "json"})}
|
44
|
+
json = JSON.parse(response.to_s)
|
45
|
+
return json["response"]["docs"]
|
46
|
+
end
|
47
|
+
|
48
|
+
# returns a Maven's triple (artifactId, groupId, version)
|
49
|
+
# from a result object
|
50
|
+
def get_maven_id_from(result)
|
51
|
+
return result["g"], result["a"], result["v"]
|
52
|
+
end
|
53
|
+
|
54
|
+
# downloads a POM from a search.maven.com search result
|
55
|
+
def download_pom(group_id, artifact_id, version)
|
56
|
+
path = "#{group_id.gsub(".", "/")}/#{artifact_id}/#{version}/#{artifact_id}-#{version}.pom"
|
57
|
+
log.debug("downloading #{path}...")
|
58
|
+
return (RestClient.get "http://search.maven.org/remotecontent", {:params => {:filepath => path}}).to_s
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
data/lib/gjp/pom.rb
CHANGED
@@ -29,6 +29,18 @@ module Gjp
|
|
29
29
|
def version
|
30
30
|
@doc.xpath("project/version/text()").to_s
|
31
31
|
end
|
32
|
+
|
33
|
+
def parent_group_id
|
34
|
+
@doc.xpath("project/parent/groupId/text()").to_s
|
35
|
+
end
|
36
|
+
|
37
|
+
def parent_artifact_id
|
38
|
+
@doc.xpath("project/parent/artifactId/text()").to_s
|
39
|
+
end
|
40
|
+
|
41
|
+
def parent_version
|
42
|
+
@doc.xpath("project/parent/version/text()").to_s
|
43
|
+
end
|
32
44
|
end
|
33
45
|
end
|
34
46
|
|
data/lib/gjp/version.rb
CHANGED
data/lib/gjp/version_matcher.rb
CHANGED
@@ -35,7 +35,11 @@ module Gjp
|
|
35
35
|
my_chunks = my_version.split /[\.\-\_ ~,]/
|
36
36
|
their_chunks_hash = Hash[
|
37
37
|
their_versions.map do |their_version|
|
38
|
-
their_chunks_for_version = their_version
|
38
|
+
their_chunks_for_version = if their_version != nil
|
39
|
+
their_version.split /[\.\-\_ ~,]/
|
40
|
+
else
|
41
|
+
[]
|
42
|
+
end
|
39
43
|
their_chunks_for_version += [nil]*[my_chunks.length - their_chunks_for_version.length, 0].max
|
40
44
|
[their_version, their_chunks_for_version]
|
41
45
|
end
|
@@ -0,0 +1,420 @@
|
|
1
|
+
<?xml version="1.0" encoding="ISO-8859-1"?>
|
2
|
+
<!--
|
3
|
+
|
4
|
+
Licensed to the Apache Software Foundation (ASF) under one or more
|
5
|
+
contributor license agreements. See the NOTICE file distributed with
|
6
|
+
this work for additional information regarding copyright ownership.
|
7
|
+
The ASF licenses this file to You under the Apache License, Version 2.0
|
8
|
+
(the "License"); you may not use this file except in compliance with
|
9
|
+
the License. You may obtain a copy of the License at
|
10
|
+
|
11
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
12
|
+
|
13
|
+
Unless required by applicable law or agreed to in writing, software
|
14
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
15
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
16
|
+
See the License for the specific language governing permissions and
|
17
|
+
limitations under the License.
|
18
|
+
|
19
|
+
-->
|
20
|
+
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
21
|
+
<modelVersion>4.0.0</modelVersion>
|
22
|
+
<parent>
|
23
|
+
<groupId>org.apache</groupId>
|
24
|
+
<artifactId>apache</artifactId>
|
25
|
+
<version>4</version>
|
26
|
+
</parent>
|
27
|
+
<groupId>org.apache.commons</groupId>
|
28
|
+
<artifactId>commons-parent</artifactId>
|
29
|
+
<packaging>pom</packaging>
|
30
|
+
<!-- TODO: dummy version. In Maven 2.1, this will be auto-versioned being a generic parent -->
|
31
|
+
<version>5</version>
|
32
|
+
<name>Commons Parent</name>
|
33
|
+
<url>http://commons.apache.org/</url>
|
34
|
+
<inceptionYear>2001</inceptionYear>
|
35
|
+
|
36
|
+
<ciManagement>
|
37
|
+
<system>continuum</system>
|
38
|
+
<url>http://vmbuild.apache.org/continuum/</url>
|
39
|
+
<notifiers>
|
40
|
+
<notifier>
|
41
|
+
<type>mail</type>
|
42
|
+
<configuration>
|
43
|
+
<address>dev@commons.apache.org</address>
|
44
|
+
</configuration>
|
45
|
+
</notifier>
|
46
|
+
</notifiers>
|
47
|
+
</ciManagement>
|
48
|
+
|
49
|
+
<distributionManagement>
|
50
|
+
<!--
|
51
|
+
This POM's parent POM (the Apache root POM) provides repositories. Unfortunately,
|
52
|
+
this allows for accidental deployments. So we disable them here by providing
|
53
|
+
a dummy repository. Use "mvn -Prc deploy" (Apache snapshot repository) or
|
54
|
+
"mvn -Prelease deploy" (Apache release repository), if you really want to deploy.
|
55
|
+
-->
|
56
|
+
<repository>
|
57
|
+
<id>dummy</id>
|
58
|
+
<name>Dummy to avoid accidental deploys</name>
|
59
|
+
<url />
|
60
|
+
</repository>
|
61
|
+
</distributionManagement>
|
62
|
+
|
63
|
+
<!--
|
64
|
+
This section *must* be overwritten by subprojects. It is only to allow
|
65
|
+
a release of the commons-parent POM.
|
66
|
+
-->
|
67
|
+
<scm>
|
68
|
+
<connection>scm:svn:http://svn.apache.org/repos/asf/commons/proper/commons-parent/tags/commons-parent-5</connection>
|
69
|
+
<developerConnection>scm:svn:https://svn.apache.org/repos/asf/commons/proper/commons-parent/tags/commons-parent-5</developerConnection>
|
70
|
+
<url>http://svn.apache.org/viewvc/commons/proper/commons-parent/tags/commons-parent-5</url>
|
71
|
+
</scm>
|
72
|
+
|
73
|
+
<mailingLists>
|
74
|
+
<mailingList>
|
75
|
+
<name>Commons Commits List</name>
|
76
|
+
<subscribe>commits-subscribe@commons.apache.org</subscribe>
|
77
|
+
<unsubscribe>commits-unsubscribe@commons.apache.org</unsubscribe>
|
78
|
+
<post>commits@commons.apache.org</post>
|
79
|
+
<archive>http://mail-archives.apache.org/mod_mbox/commons-commits/</archive>
|
80
|
+
<otherArchives>
|
81
|
+
<otherArchive>http://www.mail-archive.com/commits@commons.apache.org/</otherArchive>
|
82
|
+
</otherArchives>
|
83
|
+
</mailingList>
|
84
|
+
<mailingList>
|
85
|
+
<name>Commons Dev List</name>
|
86
|
+
<subscribe>dev-subscribe@commons.apache.org</subscribe>
|
87
|
+
<unsubscribe>dev-unsubscribe@commons.apache.org</unsubscribe>
|
88
|
+
<post>dev@commons.apache.org</post>
|
89
|
+
<archive>http://mail-archives.apache.org/mod_mbox/commons-dev/</archive>
|
90
|
+
<otherArchives>
|
91
|
+
<otherArchive>http://www.mail-archive.com/dev@commons.apache.org/</otherArchive>
|
92
|
+
<otherArchive>http://www.nabble.com/Commons---Dev-f317.html</otherArchive>
|
93
|
+
</otherArchives>
|
94
|
+
</mailingList>
|
95
|
+
<mailingList>
|
96
|
+
<name>Commons Issues List</name>
|
97
|
+
<subscribe>issues-subscribe@commons.apache.org</subscribe>
|
98
|
+
<unsubscribe>issues-unsubscribe@commons.apache.org</unsubscribe>
|
99
|
+
<post>issues@commons.apache.org</post>
|
100
|
+
<archive>http://mail-archives.apache.org/mod_mbox/commons-issues/</archive>
|
101
|
+
<otherArchives>
|
102
|
+
<otherArchive>http://www.mail-archive.com/issues@commons.apache.org/</otherArchive>
|
103
|
+
<otherArchive>http://www.nabble.com/Commons---Issues-f25499.html</otherArchive>
|
104
|
+
</otherArchives>
|
105
|
+
</mailingList>
|
106
|
+
<mailingList>
|
107
|
+
<name>Commons User List</name>
|
108
|
+
<subscribe>user-subscribe@commons.apache.org</subscribe>
|
109
|
+
<unsubscribe>user-unsubscribe@commons.apache.org</unsubscribe>
|
110
|
+
<post>user@commons.apache.org</post>
|
111
|
+
<archive>http://mail-archives.apache.org/mod_mbox/commons-user/</archive>
|
112
|
+
<otherArchives>
|
113
|
+
<otherArchive>http://www.mail-archive.com/user@commons.apache.org/</otherArchive>
|
114
|
+
<otherArchive>http://www.nabble.com/Commons---User-f319.html</otherArchive>
|
115
|
+
</otherArchives>
|
116
|
+
</mailingList>
|
117
|
+
</mailingLists>
|
118
|
+
<build>
|
119
|
+
<pluginManagement>
|
120
|
+
<plugins>
|
121
|
+
<plugin>
|
122
|
+
<groupId>org.apache.maven.plugins</groupId>
|
123
|
+
<artifactId>maven-gpg-plugin</artifactId>
|
124
|
+
<version>1.0-alpha-3</version>
|
125
|
+
</plugin>
|
126
|
+
<plugin>
|
127
|
+
<groupId>org.apache.maven.plugins</groupId>
|
128
|
+
<artifactId>maven-jar-plugin</artifactId>
|
129
|
+
<version>2.1</version>
|
130
|
+
</plugin>
|
131
|
+
<plugin>
|
132
|
+
<groupId>org.apache.maven.plugins</groupId>
|
133
|
+
<artifactId>maven-remote-resources-plugin</artifactId>
|
134
|
+
<version>1.0-alpha-6</version>
|
135
|
+
</plugin>
|
136
|
+
<plugin>
|
137
|
+
<groupId>org.apache.maven.plugins</groupId>
|
138
|
+
<artifactId>maven-source-plugin</artifactId>
|
139
|
+
<version>2.0.4</version>
|
140
|
+
</plugin>
|
141
|
+
<plugin>
|
142
|
+
<groupId>org.apache.maven.plugins</groupId>
|
143
|
+
<artifactId>maven-surefire-plugin</artifactId>
|
144
|
+
<version>2.3</version>
|
145
|
+
</plugin>
|
146
|
+
<plugin>
|
147
|
+
<groupId>org.apache.maven.plugins</groupId>
|
148
|
+
<artifactId>maven-assembly-plugin</artifactId>
|
149
|
+
<version>2.2-beta-1</version>
|
150
|
+
</plugin>
|
151
|
+
</plugins>
|
152
|
+
</pluginManagement>
|
153
|
+
<plugins>
|
154
|
+
<!-- TODO: later use toolchain support to do compilation on an external JDK 1.3+ compiler -->
|
155
|
+
<plugin>
|
156
|
+
<artifactId>maven-compiler-plugin</artifactId>
|
157
|
+
<configuration>
|
158
|
+
<source>${maven.compile.source}</source>
|
159
|
+
<target>${maven.compile.target}</target>
|
160
|
+
</configuration>
|
161
|
+
</plugin>
|
162
|
+
<plugin>
|
163
|
+
<artifactId>maven-jar-plugin</artifactId>
|
164
|
+
<configuration>
|
165
|
+
<archive>
|
166
|
+
<manifestEntries>
|
167
|
+
<Specification-Title>${project.name}</Specification-Title>
|
168
|
+
<Specification-Version>${project.version}</Specification-Version>
|
169
|
+
<Specification-Vendor>${project.organization.name}</Specification-Vendor>
|
170
|
+
<Implementation-Title>${project.name}</Implementation-Title>
|
171
|
+
<Implementation-Version>${project.version}</Implementation-Version>
|
172
|
+
<Implementation-Vendor>${project.organization.name}</Implementation-Vendor>
|
173
|
+
<Implementation-Vendor-Id>org.apache</Implementation-Vendor-Id>
|
174
|
+
<X-Compile-Source-JDK>${maven.compile.source}</X-Compile-Source-JDK>
|
175
|
+
<X-Compile-Target-JDK>${maven.compile.source}</X-Compile-Target-JDK>
|
176
|
+
</manifestEntries>
|
177
|
+
</archive>
|
178
|
+
</configuration>
|
179
|
+
</plugin>
|
180
|
+
<plugin>
|
181
|
+
<artifactId>maven-idea-plugin</artifactId>
|
182
|
+
<configuration>
|
183
|
+
<jdkLevel>${maven.compile.source}</jdkLevel>
|
184
|
+
</configuration>
|
185
|
+
</plugin>
|
186
|
+
</plugins>
|
187
|
+
</build>
|
188
|
+
|
189
|
+
<reporting>
|
190
|
+
<plugins>
|
191
|
+
<plugin>
|
192
|
+
<groupId>org.apache.maven.plugins</groupId>
|
193
|
+
<artifactId>maven-javadoc-plugin</artifactId>
|
194
|
+
<version>2.2</version>
|
195
|
+
<configuration>
|
196
|
+
<aggregate>true</aggregate>
|
197
|
+
</configuration>
|
198
|
+
</plugin>
|
199
|
+
<plugin>
|
200
|
+
<groupId>org.apache.maven.plugins</groupId>
|
201
|
+
<artifactId>maven-jxr-plugin</artifactId>
|
202
|
+
<version>2.1</version>
|
203
|
+
<configuration>
|
204
|
+
<aggregate>true</aggregate>
|
205
|
+
</configuration>
|
206
|
+
</plugin>
|
207
|
+
<plugin>
|
208
|
+
<groupId>org.apache.maven.plugins</groupId>
|
209
|
+
<artifactId>maven-site-plugin</artifactId>
|
210
|
+
<configuration>
|
211
|
+
<!-- Exclude the navigation file for Maven 1 sites
|
212
|
+
and the changes file used by the changes-plugin,
|
213
|
+
as they interfere with the site generation. -->
|
214
|
+
<moduleExcludes>
|
215
|
+
<xdoc>navigation.xml,changes.xml</xdoc>
|
216
|
+
</moduleExcludes>
|
217
|
+
</configuration>
|
218
|
+
</plugin>
|
219
|
+
<plugin>
|
220
|
+
<groupId>org.apache.maven.plugins</groupId>
|
221
|
+
<artifactId>maven-surefire-report-plugin</artifactId>
|
222
|
+
<version>2.3</version>
|
223
|
+
</plugin>
|
224
|
+
<plugin>
|
225
|
+
<groupId>org.codehaus.mojo</groupId>
|
226
|
+
<artifactId>jdepend-maven-plugin</artifactId>
|
227
|
+
<version>2.0-beta-1</version>
|
228
|
+
</plugin>
|
229
|
+
<plugin>
|
230
|
+
<groupId>org.codehaus.mojo</groupId>
|
231
|
+
<artifactId>rat-maven-plugin</artifactId>
|
232
|
+
<version>1.0-alpha-3</version>
|
233
|
+
</plugin>
|
234
|
+
</plugins>
|
235
|
+
</reporting>
|
236
|
+
|
237
|
+
<profiles>
|
238
|
+
<profile>
|
239
|
+
<id>ci</id>
|
240
|
+
<distributionManagement>
|
241
|
+
<repository>
|
242
|
+
<id>apache.snapshots</id>
|
243
|
+
<name>Apache Development Snapshot Repository</name>
|
244
|
+
<url>${commons.deployment.protocol}://people.apache.org/www/people.apache.org/repo/m2-snapshot-repository</url>
|
245
|
+
</repository>
|
246
|
+
<snapshotRepository>
|
247
|
+
<id>apache.snapshots</id>
|
248
|
+
<name>Apache Development Snapshot Repository</name>
|
249
|
+
<url>${commons.deployment.protocol}://people.apache.org/www/people.apache.org/repo/m2-snapshot-repository</url>
|
250
|
+
</snapshotRepository>
|
251
|
+
</distributionManagement>
|
252
|
+
</profile>
|
253
|
+
|
254
|
+
<profile>
|
255
|
+
<id>release</id>
|
256
|
+
<distributionManagement>
|
257
|
+
<repository>
|
258
|
+
<id>apache.releases</id>
|
259
|
+
<name>Apache Release Distribution Repository</name>
|
260
|
+
<url>${commons.deployment.protocol}://people.apache.org/www/people.apache.org/repo/m2-ibiblio-rsync-repository</url>
|
261
|
+
</repository>
|
262
|
+
</distributionManagement>
|
263
|
+
<build>
|
264
|
+
<plugins>
|
265
|
+
<!-- We want to sign the artifact, the POM, and all attached artifacts -->
|
266
|
+
<plugin>
|
267
|
+
<artifactId>maven-gpg-plugin</artifactId>
|
268
|
+
<configuration>
|
269
|
+
<passphrase>${gpg.passphrase}</passphrase>
|
270
|
+
</configuration>
|
271
|
+
<executions>
|
272
|
+
<execution>
|
273
|
+
<id>sign-artifacts</id>
|
274
|
+
<phase>verify</phase>
|
275
|
+
<goals>
|
276
|
+
<goal>sign</goal>
|
277
|
+
</goals>
|
278
|
+
</execution>
|
279
|
+
</executions>
|
280
|
+
</plugin>
|
281
|
+
<plugin>
|
282
|
+
<artifactId>maven-source-plugin</artifactId>
|
283
|
+
<executions>
|
284
|
+
<execution>
|
285
|
+
<id>create-source-jar</id>
|
286
|
+
<goals>
|
287
|
+
<goal>jar</goal>
|
288
|
+
</goals>
|
289
|
+
</execution>
|
290
|
+
</executions>
|
291
|
+
</plugin>
|
292
|
+
<plugin>
|
293
|
+
<artifactId>maven-javadoc-plugin</artifactId>
|
294
|
+
<executions>
|
295
|
+
<execution>
|
296
|
+
<id>create-javadoc-jar</id>
|
297
|
+
<goals>
|
298
|
+
<goal>jar</goal>
|
299
|
+
</goals>
|
300
|
+
<configuration>
|
301
|
+
<source>${maven.compile.source}</source>
|
302
|
+
</configuration>
|
303
|
+
</execution>
|
304
|
+
</executions>
|
305
|
+
</plugin>
|
306
|
+
<!-- We want to package license resources into the JARs -->
|
307
|
+
<plugin>
|
308
|
+
<groupId>org.apache.maven.plugins</groupId>
|
309
|
+
<artifactId>maven-remote-resources-plugin</artifactId>
|
310
|
+
<executions>
|
311
|
+
<execution>
|
312
|
+
<goals>
|
313
|
+
<goal>process</goal>
|
314
|
+
</goals>
|
315
|
+
<configuration>
|
316
|
+
<resourceBundles>
|
317
|
+
<resourceBundle>org.apache:apache-jar-resource-bundle:1.3</resourceBundle>
|
318
|
+
</resourceBundles>
|
319
|
+
</configuration>
|
320
|
+
</execution>
|
321
|
+
</executions>
|
322
|
+
</plugin>
|
323
|
+
</plugins>
|
324
|
+
</build>
|
325
|
+
</profile>
|
326
|
+
|
327
|
+
<profile>
|
328
|
+
<id>rc</id>
|
329
|
+
<distributionManagement>
|
330
|
+
<repository>
|
331
|
+
<id>apache.snapshots</id>
|
332
|
+
<name>Apache Development Snapshot Repository</name>
|
333
|
+
<url>${commons.deployment.protocol}://people.apache.org/www/people.apache.org/repo/m2-snapshot-repository</url>
|
334
|
+
</repository>
|
335
|
+
<snapshotRepository>
|
336
|
+
<id>apache.snapshots</id>
|
337
|
+
<name>Apache Development Snapshot Repository</name>
|
338
|
+
<url>${commons.deployment.protocol}://people.apache.org/www/people.apache.org/repo/m2-snapshot-repository</url>
|
339
|
+
</snapshotRepository>
|
340
|
+
</distributionManagement>
|
341
|
+
<build>
|
342
|
+
<plugins>
|
343
|
+
<!-- We want to sign the artifact, the POM, and all attached artifacts -->
|
344
|
+
<plugin>
|
345
|
+
<artifactId>maven-gpg-plugin</artifactId>
|
346
|
+
<configuration>
|
347
|
+
<passphrase>${gpg.passphrase}</passphrase>
|
348
|
+
</configuration>
|
349
|
+
<executions>
|
350
|
+
<execution>
|
351
|
+
<id>sign-artifacts</id>
|
352
|
+
<phase>verify</phase>
|
353
|
+
<goals>
|
354
|
+
<goal>sign</goal>
|
355
|
+
</goals>
|
356
|
+
</execution>
|
357
|
+
</executions>
|
358
|
+
</plugin>
|
359
|
+
<plugin>
|
360
|
+
<artifactId>maven-source-plugin</artifactId>
|
361
|
+
<executions>
|
362
|
+
<execution>
|
363
|
+
<id>create-source-jar</id>
|
364
|
+
<goals>
|
365
|
+
<goal>jar</goal>
|
366
|
+
</goals>
|
367
|
+
</execution>
|
368
|
+
</executions>
|
369
|
+
</plugin>
|
370
|
+
<plugin>
|
371
|
+
<artifactId>maven-javadoc-plugin</artifactId>
|
372
|
+
<executions>
|
373
|
+
<execution>
|
374
|
+
<id>create-javadoc-jar</id>
|
375
|
+
<goals>
|
376
|
+
<goal>jar</goal>
|
377
|
+
</goals>
|
378
|
+
<configuration>
|
379
|
+
<source>${maven.compile.source}</source>
|
380
|
+
</configuration>
|
381
|
+
</execution>
|
382
|
+
</executions>
|
383
|
+
</plugin>
|
384
|
+
<!-- We want to package license resources into the JARs -->
|
385
|
+
<plugin>
|
386
|
+
<groupId>org.apache.maven.plugins</groupId>
|
387
|
+
<artifactId>maven-remote-resources-plugin</artifactId>
|
388
|
+
<executions>
|
389
|
+
<execution>
|
390
|
+
<goals>
|
391
|
+
<goal>process</goal>
|
392
|
+
</goals>
|
393
|
+
<configuration>
|
394
|
+
<resourceBundles>
|
395
|
+
<resourceBundle>org.apache:apache-jar-resource-bundle:1.3</resourceBundle>
|
396
|
+
</resourceBundles>
|
397
|
+
</configuration>
|
398
|
+
</execution>
|
399
|
+
</executions>
|
400
|
+
</plugin>
|
401
|
+
</plugins>
|
402
|
+
</build>
|
403
|
+
</profile>
|
404
|
+
</profiles>
|
405
|
+
|
406
|
+
<properties>
|
407
|
+
|
408
|
+
<!-- Default configuration for compiler source and target JVM -->
|
409
|
+
<maven.compile.source>1.3</maven.compile.source>
|
410
|
+
<maven.compile.target>1.3</maven.compile.target>
|
411
|
+
|
412
|
+
<!--
|
413
|
+
Make the deployment protocol pluggable. This allows to switch to
|
414
|
+
other protocols like scpexe, which some users prefer over scp.
|
415
|
+
-->
|
416
|
+
<commons.deployment.protocol>scp</commons.deployment.protocol>
|
417
|
+
|
418
|
+
</properties>
|
419
|
+
|
420
|
+
</project>
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe Gjp::ParentPomGetter do
|
6
|
+
describe ".get_parent_pom" do
|
7
|
+
it "gets the the parent of a pom" do
|
8
|
+
dir_path = File.join("spec", "data", "commons-logging")
|
9
|
+
pom_path = File.join(dir_path, "pom.xml")
|
10
|
+
parent_pom_path = File.join(dir_path, "parent_pom.xml")
|
11
|
+
Gjp::ParentPomGetter.get_parent_pom(pom_path).should eq(File.read(parent_pom_path))
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe Gjp::MavenWebsite do
|
6
|
+
|
7
|
+
let(:site) { Gjp::MavenWebsite.new }
|
8
|
+
|
9
|
+
describe "#search_by_sha1" do
|
10
|
+
it "uses search.maven.org to look for poms by jar SHA1" do
|
11
|
+
result = site.search_by_sha1("546b5220622c4d9b2da45ad1899224b6ce1c8830").first
|
12
|
+
|
13
|
+
result["g"].should eq("antlr")
|
14
|
+
result["a"].should eq("antlrall")
|
15
|
+
result["v"].should eq("2.7.2")
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
describe "#search_by_name" do
|
20
|
+
it "uses search.maven.org to look for poms by keyword (name)" do
|
21
|
+
result = site.search_by_name("jruby").first
|
22
|
+
|
23
|
+
# not much to test here
|
24
|
+
result.should_not be_nil
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
describe "#search_by_group_id_and_artifact_id" do
|
29
|
+
it "uses search.maven.org to look for poms by group and artifact id" do
|
30
|
+
result = site.search_by_group_id_and_artifact_id("antlr", "antlrall")
|
31
|
+
|
32
|
+
result.should include({"id"=>"antlr:antlrall:2.7.2", "g"=>"antlr", "a"=>"antlrall", "v"=>"2.7.2", "p"=>"jar", "timestamp"=>1182142732000, "ec"=>[".jar", ".pom"]}, {"id"=>"antlr:antlrall:2.7.4", "g"=>"antlr", "a"=>"antlrall", "v"=>"2.7.4", "p"=>"jar", "timestamp"=>1131488123000, "ec"=>[".jar", ".pom"]}, {"id"=>"antlr:antlrall:2.7.1", "g"=>"antlr", "a"=>"antlrall", "v"=>"2.7.1", "p"=>"jar", "timestamp"=>1131488123000, "ec"=>[".jar", ".pom"]})
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
describe "#search_by_maven_id" do
|
37
|
+
it "uses search.maven.org to look for poms by group id, artifact id and version" do
|
38
|
+
result = site.search_by_maven_id("antlr", "antlrall", "2.7.2")
|
39
|
+
|
40
|
+
result.first["id"].should eq("antlr:antlrall:2.7.2")
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
describe "#get_maven_id_from" do
|
45
|
+
it "uses search.maven.org to look for poms" do
|
46
|
+
site.get_maven_id_from({"g" => 1, "a" => 2, "v" => 3}).should eq([1, 2, 3])
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
describe "#download_pom" do
|
51
|
+
it "gets a pom from search.maven.org" do
|
52
|
+
dir_path = File.join("spec", "data", "antlr")
|
53
|
+
pom_path = File.join(dir_path, "pom.xml")
|
54
|
+
site.download_pom("antlr", "antlrall", "2.7.2").should eq(File.read(pom_path))
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
data/spec/lib/pom_spec.rb
CHANGED
@@ -35,6 +35,24 @@ describe Gjp::Pom do
|
|
35
35
|
pom.version.should eq "1.1.1"
|
36
36
|
end
|
37
37
|
end
|
38
|
+
|
39
|
+
describe "#parent_group_id" do
|
40
|
+
it "reads the parent's group id" do
|
41
|
+
pom.parent_group_id.should eq "org.apache.commons"
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
describe "#parent_artifact_id" do
|
46
|
+
it "reads the parent's artifact id" do
|
47
|
+
pom.parent_artifact_id.should eq "commons-parent"
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
describe "#parent_version" do
|
52
|
+
it "reads the parent's version" do
|
53
|
+
pom.parent_version.should eq "5"
|
54
|
+
end
|
55
|
+
end
|
38
56
|
end
|
39
57
|
end
|
40
58
|
|
data/spec/spec_helper.rb
CHANGED
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.
|
4
|
+
version: 0.8.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-
|
12
|
+
date: 2013-06-17 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rake
|
@@ -150,30 +150,36 @@ files:
|
|
150
150
|
- .gitignore
|
151
151
|
- Gemfile
|
152
152
|
- Gemfile.lock
|
153
|
+
- LICENSE
|
153
154
|
- README.md
|
154
155
|
- Rakefile
|
155
156
|
- bin/gjp
|
156
157
|
- gjp.gemspec
|
157
158
|
- lib/gjp.rb
|
158
159
|
- lib/gjp/cli.rb
|
160
|
+
- lib/gjp/get_parent_pom.rb
|
159
161
|
- lib/gjp/get_pom.rb
|
160
162
|
- lib/gjp/get_source.rb
|
161
163
|
- lib/gjp/get_source_address.rb
|
162
164
|
- lib/gjp/logger.rb
|
165
|
+
- lib/gjp/maven_website.rb
|
163
166
|
- lib/gjp/pom.rb
|
164
167
|
- lib/gjp/version.rb
|
165
168
|
- lib/gjp/version_matcher.rb
|
166
169
|
- spec/data/antlr/antlr-2.7.2.jar
|
167
170
|
- spec/data/antlr/pom.xml
|
168
171
|
- spec/data/commons-logging/commons-logging-1.1.1.jar
|
172
|
+
- spec/data/commons-logging/parent_pom.xml
|
169
173
|
- spec/data/commons-logging/pom.xml
|
170
174
|
- spec/data/nailgun/nailgun-0.7.1.jar
|
171
175
|
- spec/data/nailgun/pom.xml
|
172
176
|
- spec/data/struts-apps/pom.xml
|
173
177
|
- spec/data/tomcat/pom.xml
|
178
|
+
- spec/lib/get_parent_pom_spec.rb
|
174
179
|
- spec/lib/get_pom_spec.rb
|
175
180
|
- spec/lib/get_source_address_spec.rb
|
176
181
|
- spec/lib/get_source_spec.rb
|
182
|
+
- spec/lib/maven_website_spec.rb
|
177
183
|
- spec/lib/pom_spec.rb
|
178
184
|
- spec/lib/version_matcher_spec.rb
|
179
185
|
- spec/spec_helper.rb
|
@@ -205,14 +211,17 @@ test_files:
|
|
205
211
|
- spec/data/antlr/antlr-2.7.2.jar
|
206
212
|
- spec/data/antlr/pom.xml
|
207
213
|
- spec/data/commons-logging/commons-logging-1.1.1.jar
|
214
|
+
- spec/data/commons-logging/parent_pom.xml
|
208
215
|
- spec/data/commons-logging/pom.xml
|
209
216
|
- spec/data/nailgun/nailgun-0.7.1.jar
|
210
217
|
- spec/data/nailgun/pom.xml
|
211
218
|
- spec/data/struts-apps/pom.xml
|
212
219
|
- spec/data/tomcat/pom.xml
|
220
|
+
- spec/lib/get_parent_pom_spec.rb
|
213
221
|
- spec/lib/get_pom_spec.rb
|
214
222
|
- spec/lib/get_source_address_spec.rb
|
215
223
|
- spec/lib/get_source_spec.rb
|
224
|
+
- spec/lib/maven_website_spec.rb
|
216
225
|
- spec/lib/pom_spec.rb
|
217
226
|
- spec/lib/version_matcher_spec.rb
|
218
227
|
- spec/spec_helper.rb
|