multi_git 0.0.1.alpha1
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/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
|