multi_git 0.0.1.alpha1
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/multi_git/backend.rb +100 -0
- data/lib/multi_git/backend_set.rb +42 -0
- data/lib/multi_git/blob.rb +52 -0
- data/lib/multi_git/builder.rb +19 -0
- data/lib/multi_git/commit.rb +185 -0
- data/lib/multi_git/directory.rb +67 -0
- data/lib/multi_git/error.rb +67 -0
- data/lib/multi_git/executeable.rb +12 -0
- data/lib/multi_git/file.rb +35 -0
- data/lib/multi_git/git_backend/blob.rb +11 -0
- data/lib/multi_git/git_backend/cmd.rb +117 -0
- data/lib/multi_git/git_backend/commit.rb +75 -0
- data/lib/multi_git/git_backend/object.rb +34 -0
- data/lib/multi_git/git_backend/ref.rb +36 -0
- data/lib/multi_git/git_backend/repository.rb +162 -0
- data/lib/multi_git/git_backend/tree.rb +22 -0
- data/lib/multi_git/git_backend.rb +19 -0
- data/lib/multi_git/handle.rb +33 -0
- data/lib/multi_git/jgit_backend/blob.rb +10 -0
- data/lib/multi_git/jgit_backend/commit.rb +45 -0
- data/lib/multi_git/jgit_backend/object.rb +48 -0
- data/lib/multi_git/jgit_backend/ref.rb +117 -0
- data/lib/multi_git/jgit_backend/repository.rb +223 -0
- data/lib/multi_git/jgit_backend/rewindeable_io.rb +33 -0
- data/lib/multi_git/jgit_backend/tree.rb +28 -0
- data/lib/multi_git/jgit_backend.rb +15 -0
- data/lib/multi_git/object.rb +59 -0
- data/lib/multi_git/ref.rb +381 -0
- data/lib/multi_git/repository.rb +190 -0
- data/lib/multi_git/rugged_backend/blob.rb +8 -0
- data/lib/multi_git/rugged_backend/commit.rb +38 -0
- data/lib/multi_git/rugged_backend/object.rb +38 -0
- data/lib/multi_git/rugged_backend/ref.rb +32 -0
- data/lib/multi_git/rugged_backend/repository.rb +160 -0
- data/lib/multi_git/rugged_backend/tree.rb +18 -0
- data/lib/multi_git/rugged_backend.rb +16 -0
- data/lib/multi_git/submodule.rb +7 -0
- data/lib/multi_git/symlink.rb +42 -0
- data/lib/multi_git/tree/builder.rb +184 -0
- data/lib/multi_git/tree.rb +144 -0
- data/lib/multi_git/tree_entry.rb +86 -0
- data/lib/multi_git/utils.rb +57 -0
- data/lib/multi_git/version.rb +3 -0
- data/lib/multi_git.rb +44 -0
- metadata +138 -0
@@ -0,0 +1,100 @@
|
|
1
|
+
require 'multi_git/utils'
|
2
|
+
require 'multi_git/error'
|
3
|
+
module MultiGit
|
4
|
+
|
5
|
+
module Backend
|
6
|
+
|
7
|
+
attr :failed, :exception
|
8
|
+
|
9
|
+
def check(description, &check)
|
10
|
+
@checks ||= []
|
11
|
+
@checks << [description, check]
|
12
|
+
end
|
13
|
+
|
14
|
+
def check!
|
15
|
+
@checks.each do |description, check|
|
16
|
+
begin
|
17
|
+
result = check.call
|
18
|
+
if result == false
|
19
|
+
@failed = description
|
20
|
+
return
|
21
|
+
end
|
22
|
+
rescue Exception => e
|
23
|
+
@failed = description
|
24
|
+
@exception = e
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
private :check, :check!
|
30
|
+
|
31
|
+
# @abstract
|
32
|
+
#
|
33
|
+
# This method implements loading the backend files.
|
34
|
+
def load!
|
35
|
+
raise NotImplementedError, "Please implement #load! for #{self}"
|
36
|
+
end
|
37
|
+
|
38
|
+
# Opens a git repository.
|
39
|
+
#
|
40
|
+
# @return [Repository]
|
41
|
+
def open(directory, options = {})
|
42
|
+
load!
|
43
|
+
open(directory, options)
|
44
|
+
end
|
45
|
+
|
46
|
+
# Tests whether this backend is available.
|
47
|
+
def available?
|
48
|
+
check!
|
49
|
+
@failed.nil?
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
|
54
|
+
module RuggedBackend
|
55
|
+
|
56
|
+
extend Backend
|
57
|
+
|
58
|
+
check "Rugged available" do
|
59
|
+
defined?(::Rugged) ||
|
60
|
+
Utils.file_loadeable?('rugged.rb')
|
61
|
+
end
|
62
|
+
|
63
|
+
def self.load!
|
64
|
+
require 'multi_git/rugged_backend'
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
68
|
+
|
69
|
+
module JGitBackend
|
70
|
+
|
71
|
+
extend Backend
|
72
|
+
|
73
|
+
check "Using Jruby" do
|
74
|
+
RUBY_ENGINE == "jruby"
|
75
|
+
end
|
76
|
+
|
77
|
+
check "Jgit available" do
|
78
|
+
defined?(Java::OrgEclipseJgitLib::Repository)
|
79
|
+
end
|
80
|
+
|
81
|
+
def self.load!
|
82
|
+
require 'multi_git/jgit_backend'
|
83
|
+
end
|
84
|
+
|
85
|
+
end
|
86
|
+
|
87
|
+
module GitBackend
|
88
|
+
|
89
|
+
extend Backend
|
90
|
+
|
91
|
+
check "Git Executeable available" do
|
92
|
+
`git --version`
|
93
|
+
end
|
94
|
+
|
95
|
+
def self.load!
|
96
|
+
require 'multi_git/git_backend'
|
97
|
+
end
|
98
|
+
|
99
|
+
end
|
100
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'multi_git/backend'
|
2
|
+
module MultiGit
|
3
|
+
|
4
|
+
# @visibility private
|
5
|
+
# @api private
|
6
|
+
class BackendSet
|
7
|
+
|
8
|
+
def initialize
|
9
|
+
@backends = {}
|
10
|
+
@priorities = {}
|
11
|
+
end
|
12
|
+
|
13
|
+
def []=(name, options={}, value)
|
14
|
+
raise ArgumentError, "Expected a MultiGit::Backend, got #{value.inspect}" unless value.respond_to? :available?
|
15
|
+
@priorities[name] = options.fetch(:priority, 0)
|
16
|
+
@backends[name] = value
|
17
|
+
end
|
18
|
+
|
19
|
+
def [](name)
|
20
|
+
if name.kind_of? Backend
|
21
|
+
return name
|
22
|
+
elsif name == :best
|
23
|
+
return best
|
24
|
+
else
|
25
|
+
return @backends[name]
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def priority(name)
|
30
|
+
@priorities[name]
|
31
|
+
end
|
32
|
+
|
33
|
+
def best
|
34
|
+
@priorities.sort_by{|_,value| -value}.find do |name, _|
|
35
|
+
be = self[name]
|
36
|
+
return be if be.available?
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'stringio'
|
2
|
+
require 'multi_git/object'
|
3
|
+
require 'multi_git/builder'
|
4
|
+
module MultiGit
|
5
|
+
module Blob
|
6
|
+
|
7
|
+
module Base
|
8
|
+
def type
|
9
|
+
:blob
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
class Builder < StringIO
|
14
|
+
include Base
|
15
|
+
include MultiGit::Builder
|
16
|
+
|
17
|
+
def initialize(content = nil)
|
18
|
+
super()
|
19
|
+
if content.kind_of? Blob
|
20
|
+
self << content.content
|
21
|
+
elsif content.kind_of? String
|
22
|
+
self << content
|
23
|
+
elsif content.kind_of? IO
|
24
|
+
IO.copy_stream(content, self)
|
25
|
+
elsif content
|
26
|
+
raise ArgumentError
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def content
|
31
|
+
string
|
32
|
+
end
|
33
|
+
|
34
|
+
def content=(value)
|
35
|
+
self.string = value.dup
|
36
|
+
end
|
37
|
+
|
38
|
+
def >>(repo)
|
39
|
+
rewind
|
40
|
+
return repo.write(read)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
include Object
|
45
|
+
include Base
|
46
|
+
|
47
|
+
def to_builder
|
48
|
+
Builder.new(self)
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'multi_git/utils'
|
2
|
+
# Builders provides simple interfaces to create complex structures like trees and commits.
|
3
|
+
module MultiGit::Builder
|
4
|
+
|
5
|
+
extend MultiGit::Utils::AbstractMethods
|
6
|
+
|
7
|
+
# @!method >>(repository)
|
8
|
+
# @abstract
|
9
|
+
# Writes the content of this builder to the given repository.
|
10
|
+
# @param [Repository] repository
|
11
|
+
# @return [MultiGit::Object] the persisted object
|
12
|
+
abstract :>>
|
13
|
+
|
14
|
+
# @return [self] self
|
15
|
+
def to_builder
|
16
|
+
self
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
@@ -0,0 +1,185 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
require 'multi_git/utils'
|
3
|
+
require 'multi_git/handle'
|
4
|
+
require 'multi_git/builder'
|
5
|
+
module MultiGit
|
6
|
+
|
7
|
+
module Commit
|
8
|
+
|
9
|
+
module Base
|
10
|
+
extend Utils::AbstractMethods
|
11
|
+
extend Forwardable
|
12
|
+
|
13
|
+
# @return String
|
14
|
+
abstract :message
|
15
|
+
|
16
|
+
# @return Tree
|
17
|
+
abstract :tree
|
18
|
+
|
19
|
+
# @return [Array<Commit>]
|
20
|
+
abstract :parents
|
21
|
+
|
22
|
+
# @return [Time]
|
23
|
+
abstract :time
|
24
|
+
# @return [Handle]
|
25
|
+
abstract :author
|
26
|
+
|
27
|
+
# @return [Time]
|
28
|
+
abstract :commit_time
|
29
|
+
# @return [Handle]
|
30
|
+
abstract :committer
|
31
|
+
|
32
|
+
delegate [:[], :/] => :tree
|
33
|
+
|
34
|
+
def type
|
35
|
+
:commit
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
# A commit builder helps creating new commits by
|
40
|
+
# providing a simple interface, sane defaults and some
|
41
|
+
# validations.
|
42
|
+
#
|
43
|
+
# You can create a new commit using the commit builder like this:
|
44
|
+
#
|
45
|
+
# @example Commit an small tree
|
46
|
+
# #setup:
|
47
|
+
# dir = `mktemp -d`
|
48
|
+
# # example:
|
49
|
+
# builder = MultiGit::Commit::Builder.new
|
50
|
+
# builder.message = "My first commit"
|
51
|
+
# builder.by "me@example.com"
|
52
|
+
# builder.tree["a_file"] = "some content"
|
53
|
+
# # builder is now ready to be inserted
|
54
|
+
# repository = MultiGit.open(dir, init: true)
|
55
|
+
# commit = repository << builder #=> be_a MultiGit::Commit
|
56
|
+
# commit.tree['a_file'].content #=> eql "some content"
|
57
|
+
# # teardown:
|
58
|
+
# `rm -rf #{dir}`
|
59
|
+
class Builder
|
60
|
+
include MultiGit::Builder
|
61
|
+
include Base
|
62
|
+
|
63
|
+
# @return (see MultiGit::Commit::Base#message)
|
64
|
+
def message(*args)
|
65
|
+
if args.any?
|
66
|
+
self.message = args.first
|
67
|
+
return self
|
68
|
+
else
|
69
|
+
return @message
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
#
|
74
|
+
# @yield allows
|
75
|
+
# @return [Tree::Builder]
|
76
|
+
def tree(&block)
|
77
|
+
@tree.instance_eval(&block) if block
|
78
|
+
@tree
|
79
|
+
end
|
80
|
+
# @return [Array<Commit::Base>]
|
81
|
+
attr :parents
|
82
|
+
|
83
|
+
# @return (see MultiGit::Commit::Base#time)
|
84
|
+
attr :time
|
85
|
+
# @return (see MultiGit::Commit::Base#commit_time)
|
86
|
+
attr :commit_time
|
87
|
+
|
88
|
+
# @return (see MultiGit::Commit::Base#author)
|
89
|
+
attr :author
|
90
|
+
# @return (see MultiGit::Commit::Base#committer)
|
91
|
+
attr :committer
|
92
|
+
|
93
|
+
attr_writer :author, :committer
|
94
|
+
attr_writer :message
|
95
|
+
|
96
|
+
# @param time [Time]
|
97
|
+
def time=(time)
|
98
|
+
raise ArgumentError, "Expected a Time, got #{time.inspect}" unless time.kind_of? Time
|
99
|
+
@time = time
|
100
|
+
end
|
101
|
+
|
102
|
+
# @param time [Time]
|
103
|
+
def commit_time=(time)
|
104
|
+
raise ArgumentError, "Expected a Time, got #{time.inspect}" unless time.kind_of? Time
|
105
|
+
@commit_time = time
|
106
|
+
end
|
107
|
+
|
108
|
+
# @param handle [Handle, String]
|
109
|
+
def committer=(handle)
|
110
|
+
@committer = parse_handle(handle)
|
111
|
+
end
|
112
|
+
|
113
|
+
# @param handle [Handle, String]
|
114
|
+
def author=(handle)
|
115
|
+
@author = parse_handle(handle)
|
116
|
+
end
|
117
|
+
|
118
|
+
# DSL method to set author and committer in one step
|
119
|
+
# @param handle [Handle, String]
|
120
|
+
# @return self
|
121
|
+
def by(handle)
|
122
|
+
self.author = self.committer = handle
|
123
|
+
return self
|
124
|
+
end
|
125
|
+
|
126
|
+
def at(time)
|
127
|
+
self.time = self.commit_time = time
|
128
|
+
return self
|
129
|
+
end
|
130
|
+
|
131
|
+
def initialize(from = nil, &block)
|
132
|
+
@parents = []
|
133
|
+
if from.kind_of? Tree
|
134
|
+
@tree = from.to_builder
|
135
|
+
elsif from.kind_of? Tree::Builder
|
136
|
+
@tree = from
|
137
|
+
elsif from.kind_of? Commit
|
138
|
+
@tree = from.tree.to_builder
|
139
|
+
@parents << from
|
140
|
+
elsif from.nil?
|
141
|
+
@tree = Tree::Builder.new
|
142
|
+
end
|
143
|
+
@author = nil
|
144
|
+
@committer = nil
|
145
|
+
@time = @commit_time = Time.now
|
146
|
+
instance_eval(&block) if block
|
147
|
+
end
|
148
|
+
|
149
|
+
def >>(repo)
|
150
|
+
new_tree = repo << tree
|
151
|
+
new_parents = parents.map{|p| repo.write(p).oid }
|
152
|
+
c = committer || author || Handle::DEFAULT
|
153
|
+
a = author || committer || Handle::DEFAULT
|
154
|
+
return repo.make_commit(
|
155
|
+
:time => time,
|
156
|
+
:author => a,
|
157
|
+
:commit_time => commit_time,
|
158
|
+
:committer => c,
|
159
|
+
:parents => new_parents,
|
160
|
+
:tree => new_tree.oid,
|
161
|
+
:message => message || "Thank you for using multi_git",
|
162
|
+
:update_ref => []
|
163
|
+
)
|
164
|
+
end
|
165
|
+
|
166
|
+
protected
|
167
|
+
|
168
|
+
def parse_handle(handle)
|
169
|
+
case(handle)
|
170
|
+
when Handle then
|
171
|
+
return handle
|
172
|
+
when String then
|
173
|
+
return Handle.parse(handle)
|
174
|
+
else
|
175
|
+
raise ArgumentError, "Expected a String or a Handle, got #{handle.inspect}"
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
end
|
180
|
+
|
181
|
+
include Base
|
182
|
+
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
@@ -0,0 +1,67 @@
|
|
1
|
+
require 'multi_git/tree'
|
2
|
+
require 'multi_git/tree_entry'
|
3
|
+
module MultiGit
|
4
|
+
|
5
|
+
class Directory < TreeEntry
|
6
|
+
|
7
|
+
module Base
|
8
|
+
include Tree::Base
|
9
|
+
def mode
|
10
|
+
Utils::MODE_DIRECTORY
|
11
|
+
end
|
12
|
+
|
13
|
+
def parent?
|
14
|
+
!@parent.nil?
|
15
|
+
end
|
16
|
+
|
17
|
+
def size
|
18
|
+
object.size
|
19
|
+
end
|
20
|
+
|
21
|
+
def entry(key)
|
22
|
+
e = object.entry(key)
|
23
|
+
e.with_parent(self) if e
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
|
28
|
+
class Builder < TreeEntry::Builder
|
29
|
+
include Tree::Builder::DSL
|
30
|
+
include Base
|
31
|
+
|
32
|
+
def make_inner(*args)
|
33
|
+
if args.any?
|
34
|
+
if args[0].kind_of?(Tree::Builder)
|
35
|
+
return args[0]
|
36
|
+
elsif args[0].kind_of?(Directory)
|
37
|
+
return args[0].object.to_builder
|
38
|
+
elsif args[0].kind_of?(Tree)
|
39
|
+
return args[0].to_builder
|
40
|
+
end
|
41
|
+
end
|
42
|
+
Tree::Builder.new(*args)
|
43
|
+
end
|
44
|
+
|
45
|
+
def entry_set(key, value)
|
46
|
+
object.entry_set(key, make_entry(key, value))
|
47
|
+
end
|
48
|
+
|
49
|
+
def entries
|
50
|
+
Hash[
|
51
|
+
object.map{|entry| [entry.name, entry.with_parent(self) ] }
|
52
|
+
]
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
|
57
|
+
include Base
|
58
|
+
|
59
|
+
def entries
|
60
|
+
@entries ||= Hash[
|
61
|
+
object.map{|entry| [entry.name, entry.with_parent(self) ] }
|
62
|
+
]
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
module MultiGit
|
2
|
+
|
3
|
+
module Error
|
4
|
+
|
5
|
+
class NotARepository < ArgumentError
|
6
|
+
include Error
|
7
|
+
end
|
8
|
+
|
9
|
+
class RepositoryNotBare < NotARepository
|
10
|
+
include Error
|
11
|
+
end
|
12
|
+
|
13
|
+
class RepositoryBare < NotARepository
|
14
|
+
include Error
|
15
|
+
end
|
16
|
+
|
17
|
+
class InvalidObjectType < ArgumentError
|
18
|
+
include Error
|
19
|
+
end
|
20
|
+
|
21
|
+
class InvalidReference < ArgumentError
|
22
|
+
include Error
|
23
|
+
end
|
24
|
+
|
25
|
+
class InvalidTraversal < ArgumentError
|
26
|
+
include Error
|
27
|
+
end
|
28
|
+
|
29
|
+
class CyclicSymlink < Exception
|
30
|
+
include Error
|
31
|
+
end
|
32
|
+
|
33
|
+
class AmbiguousReference < InvalidReference
|
34
|
+
end
|
35
|
+
|
36
|
+
class BadRevisionSyntax < InvalidReference
|
37
|
+
end
|
38
|
+
|
39
|
+
class WrongTypeForMode < Exception
|
40
|
+
include Error
|
41
|
+
|
42
|
+
def initialize(expected, actual)
|
43
|
+
super("It looks like you expected a #{expected.inspect} but referenced entry is a #{actual.inspect}")
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
class NotYetImplemented < NotImplementedError
|
48
|
+
include Error
|
49
|
+
end
|
50
|
+
|
51
|
+
class Internal < Exception
|
52
|
+
include Error
|
53
|
+
end
|
54
|
+
|
55
|
+
class ConcurrentRefUpdate < Exception
|
56
|
+
include Error
|
57
|
+
|
58
|
+
def initialize
|
59
|
+
super 'Another process has updated the ref you are currently updating.
|
60
|
+
This is unlikely to be a problem, no data got lost. You may simply retry the update.
|
61
|
+
If this happens frequently, you may have want to run "git gc" to remove clobber.'
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'multi_git/tree_entry'
|
2
|
+
require 'multi_git/blob'
|
3
|
+
require 'forwardable'
|
4
|
+
module MultiGit
|
5
|
+
|
6
|
+
class File < TreeEntry
|
7
|
+
|
8
|
+
module Base
|
9
|
+
def mode
|
10
|
+
Utils::MODE_FILE
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
class Builder < TreeEntry::Builder
|
15
|
+
include Base
|
16
|
+
|
17
|
+
def make_inner(*args)
|
18
|
+
if args.any?
|
19
|
+
if args[0].kind_of? Blob::Builder
|
20
|
+
return args[0]
|
21
|
+
elsif args[0].kind_of? Blob
|
22
|
+
return args[0].to_builder
|
23
|
+
end
|
24
|
+
end
|
25
|
+
Blob::Builder.new(*args)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
include Base
|
30
|
+
extend Forwardable
|
31
|
+
|
32
|
+
delegate (Blob.instance_methods - Object.instance_methods) => :object
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
@@ -0,0 +1,117 @@
|
|
1
|
+
require 'shellwords'
|
2
|
+
require 'open3'
|
3
|
+
require 'multi_git/error'
|
4
|
+
module MultiGit::GitBackend
|
5
|
+
|
6
|
+
class Cmd
|
7
|
+
|
8
|
+
class Error < MultiGit::Error::Internal
|
9
|
+
|
10
|
+
def self.const_missing(name)
|
11
|
+
if name =~ /\AExitCode\d+\z/
|
12
|
+
self.const_set(name, Class.new(self))
|
13
|
+
else
|
14
|
+
super
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.[](exit_code)
|
19
|
+
return const_get("ExitCode#{exit_code}")
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def initialize(command, options = {})
|
24
|
+
@cmd = command
|
25
|
+
@opts = options
|
26
|
+
end
|
27
|
+
|
28
|
+
READ_BLOCK = lambda{|io|
|
29
|
+
io.read
|
30
|
+
}
|
31
|
+
|
32
|
+
def call_env(env, *args, &block)
|
33
|
+
s = cmd(*args)
|
34
|
+
block ||= READ_BLOCK
|
35
|
+
result = nil
|
36
|
+
message = nil
|
37
|
+
status = popen_foo(env, s.join(' ')) do | stdin, stdout, stderr |
|
38
|
+
if block.arity == 1
|
39
|
+
stdin.close
|
40
|
+
result = block.call(stdout)
|
41
|
+
else
|
42
|
+
result = block.call(stdin, stdout)
|
43
|
+
end
|
44
|
+
message = stderr.read
|
45
|
+
end
|
46
|
+
if status.exitstatus == 0
|
47
|
+
return result
|
48
|
+
else
|
49
|
+
raise Error[status.exitstatus], message.chomp
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def call(*args, &block)
|
54
|
+
call_env({}, *args, &block)
|
55
|
+
end
|
56
|
+
|
57
|
+
alias [] call
|
58
|
+
|
59
|
+
private
|
60
|
+
|
61
|
+
# @api private
|
62
|
+
#
|
63
|
+
# popen3 is broken in jruby, popen4 is not available in mri :(
|
64
|
+
def popen_foo(env={}, cmd)
|
65
|
+
if IO.respond_to? :popen4
|
66
|
+
# setting the env is broken in jruby
|
67
|
+
args = ['env -i', escape_opts(env), cmd]
|
68
|
+
IO.popen4(args.join(' ')) do |_pid, *yield_args|
|
69
|
+
yield *yield_args
|
70
|
+
end
|
71
|
+
return $?
|
72
|
+
else
|
73
|
+
Open3.popen3(env, cmd,:unsetenv_others => true ) do |*yield_args,_thr|
|
74
|
+
yield *yield_args
|
75
|
+
return _thr.value
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def cmd(*args)
|
81
|
+
[ @cmd, escape_opts(@opts), escape_args(args) ].flatten
|
82
|
+
end
|
83
|
+
|
84
|
+
def escape_args(args)
|
85
|
+
args.map{|arg|
|
86
|
+
if arg.kind_of? Hash
|
87
|
+
escape_opts(arg)
|
88
|
+
elsif arg.kind_of? Array
|
89
|
+
escape_args(arg)
|
90
|
+
else
|
91
|
+
Shellwords.escape(opt_to_string(arg))
|
92
|
+
end
|
93
|
+
}
|
94
|
+
end
|
95
|
+
|
96
|
+
def escape_opts(opts)
|
97
|
+
opts.map{|k,v|
|
98
|
+
opt_to_string(k)+'='+Shellwords.escape(v)
|
99
|
+
}
|
100
|
+
end
|
101
|
+
|
102
|
+
def opt_to_string(opt)
|
103
|
+
if opt.kind_of? Symbol
|
104
|
+
s = opt.to_s
|
105
|
+
if s.size == 1
|
106
|
+
return '-'+s
|
107
|
+
else
|
108
|
+
return '--'+s.gsub('_','-')
|
109
|
+
end
|
110
|
+
else
|
111
|
+
return opt
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
end
|
116
|
+
|
117
|
+
end
|