jeremylightsmith-piston 1.9.3
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +6 -0
- data/History.txt +6 -0
- data/License.txt +20 -0
- data/Manifest.txt +86 -0
- data/README.txt +136 -0
- data/Rakefile +4 -0
- data/bin/piston +5 -0
- data/config/hoe.rb +76 -0
- data/config/requirements.rb +18 -0
- data/lib/piston.rb +18 -0
- data/lib/piston/cli.rb +279 -0
- data/lib/piston/commands.rb +4 -0
- data/lib/piston/commands/base.rb +44 -0
- data/lib/piston/commands/import.rb +69 -0
- data/lib/piston/commands/info.rb +14 -0
- data/lib/piston/commands/lock_unlock.rb +21 -0
- data/lib/piston/commands/update.rb +28 -0
- data/lib/piston/git.rb +12 -0
- data/lib/piston/git/client.rb +77 -0
- data/lib/piston/git/commit.rb +70 -0
- data/lib/piston/git/repository.rb +63 -0
- data/lib/piston/git/working_copy.rb +63 -0
- data/lib/piston/repository.rb +57 -0
- data/lib/piston/revision.rb +48 -0
- data/lib/piston/svn.rb +14 -0
- data/lib/piston/svn/client.rb +88 -0
- data/lib/piston/svn/repository.rb +67 -0
- data/lib/piston/svn/revision.rb +74 -0
- data/lib/piston/svn/working_copy.rb +55 -0
- data/lib/piston/version.rb +9 -0
- data/lib/piston/working_copy.rb +129 -0
- data/lib/subclass_responsibility_error.rb +2 -0
- data/log/.gitignore +0 -0
- data/samples/common.rb +19 -0
- data/samples/import_git_git.rb +39 -0
- data/samples/import_git_svn.rb +36 -0
- data/samples/import_svn_git.rb +29 -0
- data/samples/import_svn_svn.rb +24 -0
- data/script/destroy +14 -0
- data/script/generate +14 -0
- data/script/txt2html +74 -0
- data/setup.rb +1585 -0
- data/tasks/deployment.rake +40 -0
- data/tasks/environment.rake +7 -0
- data/tasks/samples.rake +6 -0
- data/tasks/test.rake +30 -0
- data/tasks/website.rake +17 -0
- data/test/integration/test_import_git_git.rb +96 -0
- data/test/integration/test_import_git_svn.rb +131 -0
- data/test/integration/test_import_svn_git.rb +52 -0
- data/test/integration/test_import_svn_svn.rb +43 -0
- data/test/integration_helpers.rb +33 -0
- data/test/test_helper.rb +56 -0
- data/test/unit/git/commit/test_checkout.rb +30 -0
- data/test/unit/git/commit/test_each.rb +36 -0
- data/test/unit/git/commit/test_rememberance.rb +20 -0
- data/test/unit/git/commit/test_validation.rb +33 -0
- data/test/unit/git/repository/test_at.rb +22 -0
- data/test/unit/git/repository/test_basename.rb +12 -0
- data/test/unit/git/repository/test_branchanme.rb +15 -0
- data/test/unit/git/repository/test_guessing.rb +32 -0
- data/test/unit/git/working_copy/test_copying.rb +28 -0
- data/test/unit/git/working_copy/test_creation.rb +26 -0
- data/test/unit/git/working_copy/test_existence.rb +21 -0
- data/test/unit/git/working_copy/test_finalization.rb +18 -0
- data/test/unit/git/working_copy/test_guessing.rb +34 -0
- data/test/unit/git/working_copy/test_rememberance.rb +25 -0
- data/test/unit/svn/repository/test_at.rb +18 -0
- data/test/unit/svn/repository/test_basename.rb +24 -0
- data/test/unit/svn/repository/test_guessing.rb +45 -0
- data/test/unit/svn/revision/test_checkout.rb +27 -0
- data/test/unit/svn/revision/test_each.rb +21 -0
- data/test/unit/svn/revision/test_rememberance.rb +42 -0
- data/test/unit/svn/revision/test_validation.rb +49 -0
- data/test/unit/svn/working_copy/test_copying.rb +29 -0
- data/test/unit/svn/working_copy/test_creation.rb +19 -0
- data/test/unit/svn/working_copy/test_existence.rb +26 -0
- data/test/unit/svn/working_copy/test_finalization.rb +21 -0
- data/test/unit/svn/working_copy/test_guessing.rb +17 -0
- data/test/unit/svn/working_copy/test_rememberance.rb +26 -0
- data/test/unit/test_import.rb +13 -0
- data/test/unit/test_info.rb +36 -0
- data/test/unit/test_lock_unlock.rb +46 -0
- data/test/unit/test_repository.rb +50 -0
- data/test/unit/test_revision.rb +30 -0
- data/test/unit/working_copy/test_guessing.rb +34 -0
- data/test/unit/working_copy/test_info.rb +13 -0
- data/test/unit/working_copy/test_rememberance.rb +48 -0
- data/test/unit/working_copy/test_validate.rb +68 -0
- data/website/index.html +11 -0
- data/website/index.txt +39 -0
- data/website/javascripts/rounded_corners_lite.inc.js +285 -0
- data/website/stylesheets/screen.css +138 -0
- data/website/template.rhtml +48 -0
- metadata +228 -0
@@ -0,0 +1,57 @@
|
|
1
|
+
require "piston/revision"
|
2
|
+
|
3
|
+
module Piston
|
4
|
+
class Repository
|
5
|
+
class UnhandledUrl < RuntimeError; end
|
6
|
+
|
7
|
+
class << self
|
8
|
+
def logger
|
9
|
+
@@logger ||= Log4r::Logger["handler"]
|
10
|
+
end
|
11
|
+
|
12
|
+
def guess(url)
|
13
|
+
logger.info {"Guessing the repository type of #{url.inspect}"}
|
14
|
+
|
15
|
+
handler = handlers.detect do |handler|
|
16
|
+
logger.debug {"Asking #{handler}"}
|
17
|
+
handler.understands_url?(url)
|
18
|
+
end
|
19
|
+
|
20
|
+
raise UnhandledUrl unless handler
|
21
|
+
|
22
|
+
handler.new(url)
|
23
|
+
end
|
24
|
+
|
25
|
+
@@handlers = Array.new
|
26
|
+
def add_handler(handler)
|
27
|
+
@@handlers << handler
|
28
|
+
end
|
29
|
+
|
30
|
+
def handlers
|
31
|
+
@@handlers
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
attr_reader :url
|
36
|
+
|
37
|
+
def initialize(url)
|
38
|
+
@url = url
|
39
|
+
end
|
40
|
+
|
41
|
+
def logger
|
42
|
+
self.class.logger
|
43
|
+
end
|
44
|
+
|
45
|
+
def at(revision)
|
46
|
+
raise SubclassResponsibilityError, "Piston::Repository#at should be implemented by a subclass."
|
47
|
+
end
|
48
|
+
|
49
|
+
def to_s
|
50
|
+
"Piston::Repository(#{@url})"
|
51
|
+
end
|
52
|
+
|
53
|
+
def ==(other)
|
54
|
+
url == other.url
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module Piston
|
2
|
+
class Revision
|
3
|
+
include Enumerable
|
4
|
+
|
5
|
+
class << self
|
6
|
+
def logger
|
7
|
+
@@logger ||= Log4r::Logger["handler"]
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
attr_reader :repository, :revision, :recalled_values
|
12
|
+
|
13
|
+
def initialize(repository, revision, recalled_values={})
|
14
|
+
@repository, @revision, @recalled_values = repository, revision, recalled_values
|
15
|
+
end
|
16
|
+
|
17
|
+
def name
|
18
|
+
@revision
|
19
|
+
end
|
20
|
+
|
21
|
+
def logger
|
22
|
+
self.class.logger
|
23
|
+
end
|
24
|
+
|
25
|
+
def to_s
|
26
|
+
"Piston::Revision(#{@repository.url}@#{@revision})"
|
27
|
+
end
|
28
|
+
|
29
|
+
# Retrieve a copy of this repository into +dir+.
|
30
|
+
def checkout_to(dir)
|
31
|
+
logger.debug {"Checking out #{@repository}@#{@revision} into #{dir}"}
|
32
|
+
end
|
33
|
+
|
34
|
+
# What values does this revision want to remember for the future ?
|
35
|
+
def remember_values
|
36
|
+
logger.debug {"Generating remember values"}
|
37
|
+
{}
|
38
|
+
end
|
39
|
+
|
40
|
+
# Yields each file of this revision in turn to our caller.
|
41
|
+
def each
|
42
|
+
end
|
43
|
+
|
44
|
+
# Copies +relpath+ (relative to ourselves) to +abspath+ (an absolute path).
|
45
|
+
def copy_to(relpath, abspath)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
data/lib/piston/svn.rb
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
require "piston/svn/client"
|
2
|
+
require "piston/svn/repository"
|
3
|
+
require "piston/svn/revision"
|
4
|
+
require "piston/svn/working_copy"
|
5
|
+
|
6
|
+
module Piston
|
7
|
+
module Svn
|
8
|
+
ROOT = "piston:root"
|
9
|
+
UUID = "piston:uuid"
|
10
|
+
REMOTE_REV = "piston:remote-revision"
|
11
|
+
LOCAL_REV = "piston:local-revision"
|
12
|
+
LOCKED = "piston:locked"
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
require "singleton"
|
2
|
+
|
3
|
+
module Piston
|
4
|
+
module Svn
|
5
|
+
class Client
|
6
|
+
include Singleton
|
7
|
+
|
8
|
+
class CommandError < RuntimeError; end
|
9
|
+
class Failed < CommandError; end
|
10
|
+
class BadCommand < CommandError; end
|
11
|
+
|
12
|
+
def logger
|
13
|
+
@logger ||= Log4r::Logger["handler::client"]
|
14
|
+
end
|
15
|
+
|
16
|
+
def out_logger
|
17
|
+
@out_logger ||= Log4r::Logger["handler::client::out"]
|
18
|
+
end
|
19
|
+
|
20
|
+
def svnadmin(*args)
|
21
|
+
run_cmd :svnadmin, *args
|
22
|
+
end
|
23
|
+
|
24
|
+
def svn(*args)
|
25
|
+
run_cmd :svn, *args
|
26
|
+
end
|
27
|
+
|
28
|
+
def svnlook(*args)
|
29
|
+
run_cmd :svnlook, *args
|
30
|
+
end
|
31
|
+
|
32
|
+
def svnversion(*args)
|
33
|
+
run_cmd :svnversion, *args
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
def run_cmd(executable, *args)
|
38
|
+
args.collect! {|arg| arg =~ /\s|\*|\?|"|\n|\r/ ? %Q('#{arg}') : arg}
|
39
|
+
args.collect! {|arg| arg ? arg : '""'}
|
40
|
+
cmd = %Q|#{executable} #{args.join(' ')}|
|
41
|
+
logger.debug {"> " + cmd}
|
42
|
+
|
43
|
+
original_language = ENV["LANGUAGE"]
|
44
|
+
begin
|
45
|
+
ENV["LANGUAGE"] = "C"
|
46
|
+
value = run_real(cmd)
|
47
|
+
out_logger.info {"< " + value} unless (value || "").strip.empty?
|
48
|
+
return value
|
49
|
+
ensure
|
50
|
+
ENV["LANGUAGE"] = original_language
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
begin
|
55
|
+
raise LoadError, "Not implemented on Win32 machines" if RUBY_PLATFORM =~ /mswin32/
|
56
|
+
|
57
|
+
begin
|
58
|
+
require "rubygems"
|
59
|
+
rescue LoadError
|
60
|
+
# NOP -- attempt to load without Rubygems
|
61
|
+
end
|
62
|
+
|
63
|
+
require "open4"
|
64
|
+
|
65
|
+
def run_real(cmd)
|
66
|
+
begin
|
67
|
+
pid, stdin, stdout, stderr = Open4::popen4(cmd)
|
68
|
+
_, cmdstatus = Process.waitpid2(pid)
|
69
|
+
raise CommandError, "#{cmd.inspect} exited with status: #{cmdstatus.exitstatus}\n#{stderr.read}" unless cmdstatus.success?
|
70
|
+
return stdout.read
|
71
|
+
rescue Errno::ENOENT
|
72
|
+
raise BadCommand, cmd.inspect
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
rescue LoadError
|
77
|
+
# On platforms where open4 is unavailable, we fallback to running using
|
78
|
+
# the backtick method of Kernel.
|
79
|
+
def run_real(cmd)
|
80
|
+
out = `#{cmd}`
|
81
|
+
raise BadCommand, cmd.inspect if $?.exitstatus == 127
|
82
|
+
raise Failed, "#{cmd.inspect} exited with status: #{$?.exitstatus}" unless $?.success?
|
83
|
+
out
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
require "uri"
|
2
|
+
|
3
|
+
module Piston
|
4
|
+
module Svn
|
5
|
+
class Repository < Piston::Repository
|
6
|
+
# Register ourselves as a repository handler
|
7
|
+
Piston::Repository.add_handler self
|
8
|
+
|
9
|
+
class << self
|
10
|
+
def understands_url?(url)
|
11
|
+
uri = URI.parse(url)
|
12
|
+
case uri.scheme
|
13
|
+
when "svn", /^svn\+/
|
14
|
+
true
|
15
|
+
when "http", "https", "file"
|
16
|
+
# Have to contact server to know
|
17
|
+
result = svn(:info, url) rescue :failed
|
18
|
+
result == :failed ? false : true
|
19
|
+
else
|
20
|
+
# Don't know how to handle this scheme.
|
21
|
+
# Let someone else handle it
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def client
|
26
|
+
@@client ||= Piston::Svn::Client.instance
|
27
|
+
end
|
28
|
+
|
29
|
+
def svn(*args)
|
30
|
+
client.svn(*args)
|
31
|
+
end
|
32
|
+
|
33
|
+
def repository_type
|
34
|
+
'svn'
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def svn(*args)
|
39
|
+
self.class.svn(*args)
|
40
|
+
end
|
41
|
+
|
42
|
+
def at(revision)
|
43
|
+
if revision.respond_to?(:keys) then
|
44
|
+
rev = revision[Piston::Svn::REMOTE_REV]
|
45
|
+
Piston::Svn::Revision.new(self, rev, revision)
|
46
|
+
else
|
47
|
+
case
|
48
|
+
when revision == :head
|
49
|
+
Piston::Svn::Revision.new(self, "HEAD")
|
50
|
+
when revision.to_i != 0
|
51
|
+
Piston::Svn::Revision.new(self, revision.to_i)
|
52
|
+
else
|
53
|
+
raise ArgumentError, "Invalid revision argument: #{revision.inspect}"
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def basename
|
59
|
+
if self.url =~ /trunk|branches|tags/ then
|
60
|
+
self.url.sub(%r{/(?:trunk|branches|tags).*$}, "").split("/").last
|
61
|
+
else
|
62
|
+
self.url.split("/").last
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
require "piston/revision"
|
2
|
+
require "fileutils"
|
3
|
+
|
4
|
+
module Piston
|
5
|
+
module Svn
|
6
|
+
class Revision < Piston::Revision
|
7
|
+
class InvalidRevision < RuntimeError; end
|
8
|
+
class RepositoryMoved < InvalidRevision; end
|
9
|
+
class UuidChanged < InvalidRevision; end
|
10
|
+
|
11
|
+
def client
|
12
|
+
@client ||= Piston::Svn::Client.instance
|
13
|
+
end
|
14
|
+
|
15
|
+
def svn(*args)
|
16
|
+
client.svn(*args)
|
17
|
+
end
|
18
|
+
|
19
|
+
def name
|
20
|
+
"r#{revision}"
|
21
|
+
end
|
22
|
+
|
23
|
+
def checkout_to(path)
|
24
|
+
@wcpath = path.kind_of?(Pathname) ? path : Pathname.new(path)
|
25
|
+
answer = svn(:checkout, "--revision", revision, repository.url, path)
|
26
|
+
if answer =~ /Checked out revision (\d+)[.]/ then
|
27
|
+
if revision == "HEAD" then
|
28
|
+
@revision = $1.to_i
|
29
|
+
elsif revision != $1.to_i then
|
30
|
+
raise Failed, "Did not get the revision I wanted to checkout. Subversion checked out #{$1}, I wanted #{revision}"
|
31
|
+
end
|
32
|
+
else
|
33
|
+
raise Failed, "Could not checkout revision #{revision} from #{repository.url} to #{path}\n#{answer}"
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def remember_values
|
38
|
+
str = svn(:info, "--revision", revision, repository.url)
|
39
|
+
raise Failed, "Could not get 'svn info' from #{repository.url} at revision #{revision}" if str.nil? || str.chomp.strip.empty?
|
40
|
+
info = YAML.load(str)
|
41
|
+
{ Piston::Svn::UUID => info["Repository UUID"],
|
42
|
+
Piston::Svn::REMOTE_REV => info["Revision"]}
|
43
|
+
end
|
44
|
+
|
45
|
+
def each
|
46
|
+
raise ArgumentError, "Revision #{revision} of #{repository.url} was never checked out -- can't iterate over files" unless @wcpath
|
47
|
+
|
48
|
+
svn(:ls, "--recursive", @wcpath).each do |relpath|
|
49
|
+
next if relpath =~ %r{/$}
|
50
|
+
yield relpath.chomp
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def copy_to(relpath, abspath)
|
55
|
+
raise ArgumentError, "Revision #{revision} of #{repository.url} was never checked out -- can't iterate over files" unless @wcpath
|
56
|
+
|
57
|
+
Pathname.new(abspath).dirname.mkpath
|
58
|
+
FileUtils.cp(@wcpath + relpath, abspath)
|
59
|
+
end
|
60
|
+
|
61
|
+
def validate!
|
62
|
+
data = svn(:info, "--revision", revision, repository.url)
|
63
|
+
info = YAML.load(data)
|
64
|
+
actual_uuid = info["Repository UUID"]
|
65
|
+
raise RepositoryMoved, "Repository at #{repository.url} does not exist anymore:\n#{data}" if actual_uuid.blank?
|
66
|
+
raise UuidChanged, "Expected repository at #{repository.url} to have UUID #{recalled_uuid} but found #{actual_uuid}" if recalled_uuid != actual_uuid
|
67
|
+
end
|
68
|
+
|
69
|
+
def recalled_uuid
|
70
|
+
recalled_values[Piston::Svn::UUID]
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require "yaml"
|
2
|
+
|
3
|
+
module Piston
|
4
|
+
module Svn
|
5
|
+
class WorkingCopy < Piston::WorkingCopy
|
6
|
+
# Register ourselves as a handler for working copies
|
7
|
+
Piston::WorkingCopy.add_handler self
|
8
|
+
|
9
|
+
class << self
|
10
|
+
def understands_dir?(dir)
|
11
|
+
result = svn(:info, dir) rescue :failed
|
12
|
+
result == :failed ? false : true
|
13
|
+
end
|
14
|
+
|
15
|
+
def client
|
16
|
+
@@client ||= Piston::Svn::Client.instance
|
17
|
+
end
|
18
|
+
|
19
|
+
def svn(*args)
|
20
|
+
client.svn(*args)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def svn(*args)
|
25
|
+
self.class.svn(*args)
|
26
|
+
end
|
27
|
+
|
28
|
+
def exist?
|
29
|
+
result = svn(:info, path) rescue :failed
|
30
|
+
logger.debug {"result: #{result.inspect}"}
|
31
|
+
return false if result == :failed
|
32
|
+
return false if result.nil? || result.chomp.strip.empty?
|
33
|
+
return true if YAML.load(result).has_key?("Path")
|
34
|
+
end
|
35
|
+
|
36
|
+
def create
|
37
|
+
svn(:mkdir, path)
|
38
|
+
end
|
39
|
+
|
40
|
+
def after_remember(path)
|
41
|
+
info = svn(:info, path)
|
42
|
+
return unless info =~ /\(not a versioned resource\)/i || info.empty?
|
43
|
+
svn(:add, path)
|
44
|
+
end
|
45
|
+
|
46
|
+
def finalize
|
47
|
+
targets = []
|
48
|
+
Dir[path + "*"].each do |item|
|
49
|
+
svn(:add, item)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,129 @@
|
|
1
|
+
module Piston
|
2
|
+
class WorkingCopy
|
3
|
+
class UnhandledWorkingCopy < RuntimeError; end
|
4
|
+
class NotWorkingCopy < RuntimeError; end
|
5
|
+
|
6
|
+
class << self
|
7
|
+
def logger
|
8
|
+
@@logger ||= Log4r::Logger["handler"]
|
9
|
+
end
|
10
|
+
|
11
|
+
def guess(path)
|
12
|
+
path = path.kind_of?(Pathname) ? path : Pathname.new(path.to_s)
|
13
|
+
logger.info {"Guessing the working copy type of #{path.inspect}"}
|
14
|
+
handler = handlers.detect do |handler|
|
15
|
+
logger.debug {"Asking #{handler.name} if it understands #{path}"}
|
16
|
+
handler.understands_dir?(path)
|
17
|
+
end
|
18
|
+
|
19
|
+
raise UnhandledWorkingCopy, "Don't know what working copy type #{path} is." if handler.nil?
|
20
|
+
handler.new(path)
|
21
|
+
end
|
22
|
+
|
23
|
+
@@handlers = Array.new
|
24
|
+
def add_handler(handler)
|
25
|
+
@@handlers << handler
|
26
|
+
end
|
27
|
+
|
28
|
+
def handlers
|
29
|
+
@@handlers
|
30
|
+
end
|
31
|
+
private :handlers
|
32
|
+
end
|
33
|
+
|
34
|
+
attr_reader :path
|
35
|
+
|
36
|
+
def initialize(path)
|
37
|
+
@path = path.kind_of?(Pathname) ? path : Pathname.new(path)
|
38
|
+
logger.debug {"Initialized on #{@path}"}
|
39
|
+
end
|
40
|
+
|
41
|
+
def logger
|
42
|
+
self.class.logger
|
43
|
+
end
|
44
|
+
|
45
|
+
def to_s
|
46
|
+
"Piston::WorkingCopy(#{@path})"
|
47
|
+
end
|
48
|
+
|
49
|
+
def exist?
|
50
|
+
@path.exist? && @path.directory?
|
51
|
+
end
|
52
|
+
|
53
|
+
def pistonized?
|
54
|
+
yaml_path.exist? && yaml_path.file?
|
55
|
+
end
|
56
|
+
|
57
|
+
def validate!
|
58
|
+
raise NotWorkingCopy unless self.pistonized?
|
59
|
+
self
|
60
|
+
end
|
61
|
+
|
62
|
+
# Creates the initial working copy for pistonizing a new repository.
|
63
|
+
def create
|
64
|
+
logger.debug {"Creating working copy at #{path}"}
|
65
|
+
end
|
66
|
+
|
67
|
+
# Copy files from +revision+. +revision+ must
|
68
|
+
# #respond_to?(:each), and return each file that is to be copied.
|
69
|
+
# Only files must be returned.
|
70
|
+
#
|
71
|
+
# Each item yielded by Revision#each must be a relative path.
|
72
|
+
#
|
73
|
+
# WorkingCopy will call Revision#copy_to with the full path to where the
|
74
|
+
# file needs to be copied.
|
75
|
+
def copy_from(revision)
|
76
|
+
revision.each do |relpath|
|
77
|
+
target = path + relpath
|
78
|
+
target.dirname.mkdir rescue nil
|
79
|
+
|
80
|
+
logger.debug {"Copying #{relpath} to #{target}"}
|
81
|
+
revision.copy_to(relpath, target)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
# Stores a Hash of values that can be retrieved later.
|
86
|
+
def remember(values, handler_values)
|
87
|
+
values["format"] = 1
|
88
|
+
|
89
|
+
# Stringify keys
|
90
|
+
values.keys.each do |key|
|
91
|
+
values[key.to_s] = values.delete(key)
|
92
|
+
end
|
93
|
+
|
94
|
+
logger.debug {"Remembering #{values.inspect} as well as #{handler_values.inspect}"}
|
95
|
+
File.open(yaml_path, "wb") do |f|
|
96
|
+
f.write(values.merge("handler" => handler_values).to_yaml)
|
97
|
+
end
|
98
|
+
|
99
|
+
logger.debug {"Calling \#after_remember on #{yaml_path}"}
|
100
|
+
after_remember(yaml_path)
|
101
|
+
end
|
102
|
+
|
103
|
+
# Callback after #remember is done, to do whatever the
|
104
|
+
# working copy needs to do with the file.
|
105
|
+
def after_remember(path)
|
106
|
+
end
|
107
|
+
|
108
|
+
# Recalls a Hash of values from the working copy.
|
109
|
+
def recall
|
110
|
+
YAML.load(File.read(yaml_path))
|
111
|
+
end
|
112
|
+
|
113
|
+
def finalize
|
114
|
+
logger.debug {"Finalizing #{path}"}
|
115
|
+
end
|
116
|
+
|
117
|
+
# Returns basic information about this working copy.
|
118
|
+
def info
|
119
|
+
recall
|
120
|
+
end
|
121
|
+
|
122
|
+
|
123
|
+
protected
|
124
|
+
# The path to the piston YAML file.
|
125
|
+
def yaml_path
|
126
|
+
path + ".piston.yml"
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|