chrysalis 0.1.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/LICENSE +19 -0
- data/README +5 -0
- data/Rakefile +58 -0
- data/lib/chrysalis/ar/tar.rb +56 -0
- data/lib/chrysalis/ar/tar_bz2.rb +32 -0
- data/lib/chrysalis/ar/tar_gz.rb +51 -0
- data/lib/chrysalis/ar/zip.rb +48 -0
- data/lib/chrysalis/ar.rb +4 -0
- data/lib/chrysalis/archive.rb +55 -0
- data/lib/chrysalis/core_ext/string.rb +19 -0
- data/lib/chrysalis/errors.rb +18 -0
- data/lib/chrysalis/loader.rb +151 -0
- data/lib/chrysalis/manifest.rb +193 -0
- data/lib/chrysalis/project.rb +129 -0
- data/lib/chrysalis/rake_ext/intercept.rb +90 -0
- data/lib/chrysalis/repository.rb +142 -0
- data/lib/chrysalis/task.rb +78 -0
- data/lib/chrysalis/vcs/file/repository.rb +63 -0
- data/lib/chrysalis/vcs/file.rb +1 -0
- data/lib/chrysalis/vcs/http/repository.rb +67 -0
- data/lib/chrysalis/vcs/http.rb +1 -0
- data/lib/chrysalis/vcs/svn/repository.rb +88 -0
- data/lib/chrysalis/vcs/svn.rb +1 -0
- data/lib/chrysalis/vcs.rb +3 -0
- data/lib/chrysalis/version.rb +3 -0
- data/lib/chrysalis.rb +28 -0
- metadata +96 -0
data/LICENSE
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
Copyright (c) 2007 Jared Hanson
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
+
of this software and associated documentation files (the "Software"), to deal
|
5
|
+
in the Software without restriction, including without limitation the rights
|
6
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7
|
+
copies of the Software, and to permit persons to whom the Software is
|
8
|
+
furnished to do so, subject to the following conditions:
|
9
|
+
|
10
|
+
The above copyright notice and this permission notice shall be included in
|
11
|
+
all copies or substantial portions of the Software.
|
12
|
+
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19
|
+
THE SOFTWARE.
|
data/README
ADDED
data/Rakefile
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
require 'rake/clean'
|
4
|
+
require 'rake/gempackagetask'
|
5
|
+
require 'rake/rdoctask'
|
6
|
+
require 'rake/testtask'
|
7
|
+
|
8
|
+
PKG_NAME = 'chrysalis'
|
9
|
+
PKG_VERSION = '0.1.0'
|
10
|
+
|
11
|
+
PKG_FILES = FileList[
|
12
|
+
'[A-Z]*',
|
13
|
+
'lib/**/*'
|
14
|
+
]
|
15
|
+
|
16
|
+
|
17
|
+
spec = Gem::Specification.new do |s|
|
18
|
+
s.name = PKG_NAME
|
19
|
+
s.version = PKG_VERSION
|
20
|
+
s.files = PKG_FILES
|
21
|
+
s.add_dependency('rake', '>= 0.7.3')
|
22
|
+
s.add_dependency('gratr', '>= 0.4.3')
|
23
|
+
|
24
|
+
s.author = "Jared Hanson"
|
25
|
+
s.email = "jaredhanson@gmail.com"
|
26
|
+
s.homepage = "http://chrysalis.rubyforge.org/"
|
27
|
+
s.rubyforge_project = "chrysalis"
|
28
|
+
|
29
|
+
s.summary = "Simple dependency management with Ruby and Rake."
|
30
|
+
end
|
31
|
+
|
32
|
+
Rake::GemPackageTask.new(spec) do |pkg|
|
33
|
+
pkg.gem_spec = spec
|
34
|
+
end
|
35
|
+
|
36
|
+
Rake::RDocTask.new do |rdoc|
|
37
|
+
rdoc.rdoc_dir = 'doc'
|
38
|
+
rdoc.rdoc_files.include('README')
|
39
|
+
rdoc.rdoc_files.include('lib/chrysalis.rb')
|
40
|
+
rdoc.rdoc_files.include('lib/chrysalis/**/*.rb')
|
41
|
+
end
|
42
|
+
|
43
|
+
desc "Preview the rdoc HTML files in web browser"
|
44
|
+
task :preview_rdoc do
|
45
|
+
sh "heel -r doc" + (RUBY_PLATFORM.match(/mswin/) ? " 2>&1" : "")
|
46
|
+
end
|
47
|
+
task :preview_rdoc => :rdoc
|
48
|
+
|
49
|
+
desc "Publish the rdoc HTML files to RubyForge"
|
50
|
+
task :publish_rdoc do
|
51
|
+
sh "scp -r doc/* jaredhanson@rubyforge.org:/var/www/gforge-projects/chrysalis/api"
|
52
|
+
end
|
53
|
+
task :publish_rdoc => :rdoc
|
54
|
+
|
55
|
+
Rake::TestTask.new do |test|
|
56
|
+
test.libs << "test"
|
57
|
+
test.test_files = FileList['test/test_suite.rb']
|
58
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
require 'chrysalis/archive'
|
2
|
+
require 'chrysalis/core_ext/string'
|
3
|
+
|
4
|
+
|
5
|
+
module Chrysalis
|
6
|
+
module Ar
|
7
|
+
|
8
|
+
# Implements support for extracting tar archives.
|
9
|
+
#
|
10
|
+
# Archives of this type are identified by the file extension <em>tar</em>.
|
11
|
+
class TarArchive < Archive
|
12
|
+
|
13
|
+
EXTENSION = ".tar".freeze
|
14
|
+
|
15
|
+
def self.extracts?(path)
|
16
|
+
path.end? EXTENSION
|
17
|
+
end
|
18
|
+
|
19
|
+
def initialize(path, params = {})
|
20
|
+
super
|
21
|
+
@extract_to = params[:extract_to]
|
22
|
+
@extracts_into = params[:extracts_into]
|
23
|
+
@noop = params[:noop]
|
24
|
+
end
|
25
|
+
|
26
|
+
def extract(to = '.')
|
27
|
+
dir = Pathname.new(to)
|
28
|
+
dir = dir + @extract_to if @extract_to
|
29
|
+
|
30
|
+
unless @noop
|
31
|
+
FileUtils.mkpath dir
|
32
|
+
|
33
|
+
bin = (RUBY_PLATFORM.match(/mswin/) ? "bsdtar" : "tar")
|
34
|
+
|
35
|
+
puts "Extracting #{@path} ..."
|
36
|
+
sh "#{bin} #{self.flags} #{@path} -C #{dir.cleanpath}"
|
37
|
+
end
|
38
|
+
|
39
|
+
ex_path = Pathname.new(dir).join(self.extracts_into)
|
40
|
+
ex_path.cleanpath.to_s
|
41
|
+
end
|
42
|
+
|
43
|
+
protected
|
44
|
+
def flags
|
45
|
+
"xvf"
|
46
|
+
end
|
47
|
+
|
48
|
+
def extracts_into
|
49
|
+
return @extracts_into if @extracts_into
|
50
|
+
Pathname.new(@path).basename(EXTENSION)
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'chrysalis/ar/tar'
|
2
|
+
|
3
|
+
|
4
|
+
module Chrysalis
|
5
|
+
module Ar
|
6
|
+
|
7
|
+
# Implements support for extracting tar archives compressed with bzip2
|
8
|
+
# compression.
|
9
|
+
#
|
10
|
+
# Archives of this type are identified by the file extension <em>.tar.bz2</em>.
|
11
|
+
class TarBz2Archive < TarArchive
|
12
|
+
|
13
|
+
EXTENSION = ".tar.bz2".freeze
|
14
|
+
|
15
|
+
def self.extracts?(path)
|
16
|
+
path.end? EXTENSION
|
17
|
+
end
|
18
|
+
|
19
|
+
protected
|
20
|
+
def flags
|
21
|
+
"xjvf"
|
22
|
+
end
|
23
|
+
|
24
|
+
def extracts_into
|
25
|
+
return @extracts_into if @extracts_into
|
26
|
+
Pathname.new(@path).basename(EXTENSION)
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'chrysalis/ar/tar'
|
2
|
+
|
3
|
+
|
4
|
+
module Chrysalis
|
5
|
+
module Ar
|
6
|
+
|
7
|
+
# Implements support for extracting tar archives compressed with gzip
|
8
|
+
# compression.
|
9
|
+
#
|
10
|
+
# Archives of this type are identified by the file extension <em>.tar.gz</em>.
|
11
|
+
class TarGzArchive < TarArchive
|
12
|
+
|
13
|
+
EXTENSION = ".tar.gz".freeze
|
14
|
+
|
15
|
+
def self.extracts?(path)
|
16
|
+
path.end? EXTENSION
|
17
|
+
end
|
18
|
+
|
19
|
+
protected
|
20
|
+
def flags
|
21
|
+
"xzvf"
|
22
|
+
end
|
23
|
+
|
24
|
+
def extracts_into
|
25
|
+
return @extracts_into if @extracts_into
|
26
|
+
Pathname.new(@path).basename(EXTENSION)
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
|
31
|
+
|
32
|
+
# Implements support for extracting tar archives compressed with gzip
|
33
|
+
# compression.
|
34
|
+
#
|
35
|
+
# Archives of this type are identified by the file extension <em>.tgz</em>.
|
36
|
+
class TgzArchive < TarGzArchive
|
37
|
+
EXTENSION = ".tgz".freeze
|
38
|
+
|
39
|
+
def self.extracts?(path)
|
40
|
+
path.end? EXTENSION
|
41
|
+
end
|
42
|
+
|
43
|
+
protected
|
44
|
+
def extracts_into
|
45
|
+
return @extracts_into if @extracts_into
|
46
|
+
Pathname.new(@path).basename(EXTENSION)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'chrysalis/archive'
|
2
|
+
require 'chrysalis/core_ext/string'
|
3
|
+
|
4
|
+
|
5
|
+
module Chrysalis
|
6
|
+
module Ar
|
7
|
+
|
8
|
+
# Implements support for extracting ZIP archives.
|
9
|
+
#
|
10
|
+
# Archives of this type are identified by the file extension <em>.zip</em>.
|
11
|
+
class ZipArchive < Archive
|
12
|
+
|
13
|
+
EXTENSION = ".zip".freeze
|
14
|
+
|
15
|
+
def self.extracts?(path)
|
16
|
+
path.end? EXTENSION
|
17
|
+
end
|
18
|
+
|
19
|
+
def initialize(path, params = {})
|
20
|
+
super
|
21
|
+
@extract_to = params[:extract_to]
|
22
|
+
@extracts_into = params[:extracts_into]
|
23
|
+
@noop = params[:noop]
|
24
|
+
end
|
25
|
+
|
26
|
+
def extract(to = '.')
|
27
|
+
dir = Pathname.new(to)
|
28
|
+
dir = dir + @extract_to if @extract_to
|
29
|
+
|
30
|
+
unless @noop
|
31
|
+
puts "Extracting #{@path} ..."
|
32
|
+
sh "unzip #{@path} -d #{dir.cleanpath}"
|
33
|
+
end
|
34
|
+
|
35
|
+
ex_path = Pathname.new(dir).join(extracts_into)
|
36
|
+
ex_path.cleanpath.to_s
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
def extracts_into
|
41
|
+
return @extracts_into if @extracts_into
|
42
|
+
Pathname.new(@path).basename(EXTENSION)
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
end
|
data/lib/chrysalis/ar.rb
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
require 'pathname'
|
2
|
+
require 'chrysalis/errors'
|
3
|
+
|
4
|
+
|
5
|
+
module Chrysalis
|
6
|
+
|
7
|
+
class Archive
|
8
|
+
@@archives = []
|
9
|
+
|
10
|
+
def self.inherited(archive)
|
11
|
+
@@archives << archive
|
12
|
+
end
|
13
|
+
|
14
|
+
# Extracts the archive, located at +path+, to the directory +to+ (which
|
15
|
+
# defaults to the current directory). An optional set of parameters is
|
16
|
+
# accepted as +params+.
|
17
|
+
#
|
18
|
+
# Returns the directory the archive was extracted into.
|
19
|
+
#
|
20
|
+
# The return value assumes standard conventions are followed by the archive,
|
21
|
+
# namely that the directory is the name of the archive file, without the
|
22
|
+
# extension.
|
23
|
+
#
|
24
|
+
# For example, given an archive file libfoo-1.4.2.tar.gz, the directory
|
25
|
+
# returned would be libfoo-1.4.2.
|
26
|
+
#
|
27
|
+
# For archives that don't follow this practice, parameters are available
|
28
|
+
# to override default behavior.
|
29
|
+
def self.extract(path, to = '.', params = {})
|
30
|
+
pn = Pathname.new(path)
|
31
|
+
return pn.cleanpath.to_s if pn.directory?
|
32
|
+
return pn.cleanpath.to_s if params[:noop]
|
33
|
+
|
34
|
+
@@archives.reverse.each { |archive|
|
35
|
+
return archive.new(path, params).extract(to) if archive.extracts?(path)
|
36
|
+
}
|
37
|
+
raise ArchiveError, "Unknown archive format. (ext: #{pn.extname})"
|
38
|
+
end
|
39
|
+
|
40
|
+
|
41
|
+
def self.extracts?(path)
|
42
|
+
false
|
43
|
+
end
|
44
|
+
|
45
|
+
def initialize(path, params = {})
|
46
|
+
@path = path
|
47
|
+
end
|
48
|
+
|
49
|
+
def extract(to = '.')
|
50
|
+
raise UnimplementedError, "Archive#extract not implemented"
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
class String
|
2
|
+
|
3
|
+
def begin?(other_str)
|
4
|
+
self.index(other_str) == 0
|
5
|
+
end
|
6
|
+
|
7
|
+
def end?(other_str)
|
8
|
+
i = self.rindex(other_str)
|
9
|
+
return false if i.nil?
|
10
|
+
return (self.length == i + other_str.length)
|
11
|
+
end
|
12
|
+
|
13
|
+
def concat_sep(other_str, sep)
|
14
|
+
return if ! other_str
|
15
|
+
self.concat(sep) if ! empty?
|
16
|
+
self.concat(other_str)
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Chrysalis
|
2
|
+
|
3
|
+
class ManifestError < RuntimeError
|
4
|
+
end
|
5
|
+
|
6
|
+
class RepositoryError < RuntimeError
|
7
|
+
end
|
8
|
+
|
9
|
+
class ArchiveError < RuntimeError
|
10
|
+
end
|
11
|
+
|
12
|
+
class ParameterError < RuntimeError
|
13
|
+
end
|
14
|
+
|
15
|
+
class UnimplementedError < NameError
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
@@ -0,0 +1,151 @@
|
|
1
|
+
module Chrysalis
|
2
|
+
|
3
|
+
# Loader is responsible for loading rakefiles.
|
4
|
+
#
|
5
|
+
# The loader loads the rakefiles defined by dependencies. The tasks defined
|
6
|
+
# within a dependency rakefile are intercepted and re-synthesized as
|
7
|
+
# <em>all:*</em> tasks.
|
8
|
+
#
|
9
|
+
# Additionally, a hook is declared in order to receive notification that the
|
10
|
+
# main rakefile has been loaded. The tasks in the main rakefile serve to seed
|
11
|
+
# the <em>all:</em> namespace with tasks, prior to retrieving any
|
12
|
+
# dependencies.
|
13
|
+
|
14
|
+
class Loader
|
15
|
+
include Singleton
|
16
|
+
|
17
|
+
def initialize
|
18
|
+
@loading = :main
|
19
|
+
@icept_scope = []
|
20
|
+
@icept_tasks = []
|
21
|
+
@last_comment = nil
|
22
|
+
|
23
|
+
Chrysalis.hook :rakefile_loaded, self.method(:on_rakefile_loaded).to_proc
|
24
|
+
end
|
25
|
+
|
26
|
+
# Returns the URL of the project currently being loaded.
|
27
|
+
def loading
|
28
|
+
@loading
|
29
|
+
end
|
30
|
+
|
31
|
+
# Loads a dependency's rakefile.
|
32
|
+
#
|
33
|
+
# The +url+ is the location of the repository from which the dependency was
|
34
|
+
# retrieved. The +working_copy+ is the WorkingCopy retrieved.
|
35
|
+
#
|
36
|
+
# This function will search for and load the rakefile found in the working
|
37
|
+
# copy's path, if one exists.
|
38
|
+
def load(url, working_copy)
|
39
|
+
@loading = url
|
40
|
+
|
41
|
+
Rake::Application::DEFAULT_RAKEFILES.each do |file|
|
42
|
+
path = Pathname.new(working_copy.path).join(file)
|
43
|
+
|
44
|
+
if File.exist?(path)
|
45
|
+
Kernel.load(path, true)
|
46
|
+
return
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
ensure
|
51
|
+
# If the manifest does not contain an entry for the URL being loaded, one
|
52
|
+
# of two things occured:
|
53
|
+
# 1. a project was not instantiated in the rakefile -- or --
|
54
|
+
# 2. no rakefile existed in the working copy's path
|
55
|
+
#
|
56
|
+
# In either case, an empty project will be created as a placeholder.
|
57
|
+
|
58
|
+
if Chrysalis::Manifest.instance[@loading].nil?
|
59
|
+
proj = Project.new
|
60
|
+
end
|
61
|
+
|
62
|
+
Chrysalis::Manifest.instance[@loading].working_copy = working_copy
|
63
|
+
Chrysalis.post :rakefile_loaded
|
64
|
+
end
|
65
|
+
|
66
|
+
def on_rakefile_loaded # :nodoc:
|
67
|
+
return if Chrysalis::Manifest.instance[@loading].nil?
|
68
|
+
|
69
|
+
@icept_tasks.each do |task|
|
70
|
+
Chrysalis::Manifest.instance[@loading].task(task[0], task[1])
|
71
|
+
Chrysalis::TaskManager.synthesize_task(task[0], task[1])
|
72
|
+
end
|
73
|
+
|
74
|
+
ensure
|
75
|
+
@loading = :main
|
76
|
+
@icept_tasks = []
|
77
|
+
end
|
78
|
+
|
79
|
+
def task_intercept(name)
|
80
|
+
scoped_name = (@icept_scope + [name]).join(':')
|
81
|
+
@icept_tasks << [scoped_name, @last_comment]
|
82
|
+
@last_comment = nil
|
83
|
+
yield if @loading == :main
|
84
|
+
end
|
85
|
+
|
86
|
+
def file_task_intercept(name)
|
87
|
+
@icept_tasks << [name, @last_comment]
|
88
|
+
@last_comment = nil
|
89
|
+
yield if @loading == :main
|
90
|
+
end
|
91
|
+
|
92
|
+
def desc_intercept(comment)
|
93
|
+
@last_comment = comment
|
94
|
+
yield if @loading == :main
|
95
|
+
end
|
96
|
+
|
97
|
+
#--
|
98
|
+
# TODO: If name is nil, Rake generates an anonymous namespace. Chrysalis
|
99
|
+
# currently does not support this feature. In practice, anonymous
|
100
|
+
# namespaces are rarely used. However, support should be implemented
|
101
|
+
# for maximum compatibility.
|
102
|
+
def namespace_intercept(name, orig_block)
|
103
|
+
@icept_scope.push(name)
|
104
|
+
|
105
|
+
# Call the original block given to the namespace. This allows is to
|
106
|
+
# progress deeper, intercepting further tasks within the namespace.
|
107
|
+
#
|
108
|
+
# TODO: Rake instantiates a NameSpace, which is yeiled as a parameter.
|
109
|
+
# In practice, most code doesn't expect this parameter. However,
|
110
|
+
# support for it is currently unimplemented, but should be
|
111
|
+
# implemented for maximum compatibility.
|
112
|
+
#
|
113
|
+
# Example:
|
114
|
+
# orig_block.call(a_namespace)
|
115
|
+
if @loading != :main
|
116
|
+
orig_block.call
|
117
|
+
else
|
118
|
+
yield
|
119
|
+
end
|
120
|
+
|
121
|
+
ensure
|
122
|
+
@icept_scope.pop
|
123
|
+
end
|
124
|
+
|
125
|
+
def rule_intercept
|
126
|
+
yield if @loading == :main
|
127
|
+
end
|
128
|
+
|
129
|
+
end
|
130
|
+
|
131
|
+
|
132
|
+
module Hookable
|
133
|
+
@@handlers = []
|
134
|
+
|
135
|
+
def hook(evt, proc)
|
136
|
+
@@handlers << [evt, proc]
|
137
|
+
end
|
138
|
+
|
139
|
+
def post(evt, *args)
|
140
|
+
callbacks = @@handlers.select { |e, ignore| evt == e }
|
141
|
+
callbacks.each do |ignore, proc|
|
142
|
+
proc.call(*args)
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
class << self
|
148
|
+
include Hookable
|
149
|
+
end
|
150
|
+
|
151
|
+
end
|