cliver 0.2.0 → 0.2.1
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/cliver.rb +19 -2
- data/lib/cliver/dependency.rb +29 -11
- data/lib/cliver/detector.rb +7 -0
- data/lib/cliver/version.rb +1 -1
- data/lib/core_ext/file.rb +17 -0
- data/spec/cliver_spec.rb +91 -17
- metadata +3 -2
data/lib/cliver.rb
CHANGED
@@ -1,4 +1,7 @@
|
|
1
1
|
# encoding: utf-8
|
2
|
+
|
3
|
+
require File.expand_path('../core_ext/file', __FILE__)
|
4
|
+
|
2
5
|
require 'cliver/version'
|
3
6
|
require 'cliver/dependency'
|
4
7
|
require 'cliver/detector'
|
@@ -37,14 +40,28 @@ module Cliver
|
|
37
40
|
# @overload (see Cliver::Dependency#initialize)
|
38
41
|
# @param (see Cliver::Dependency#initialize)
|
39
42
|
# @option options [Boolean] :strict (true) @see Cliver::Dependency::initialize
|
40
|
-
# @raise (see Cliver::Dependency#
|
41
|
-
# @return (see Cliver::Dependency#
|
43
|
+
# @raise (see Cliver::Dependency#detect!)
|
44
|
+
# @return (see Cliver::Dependency#detect!)
|
42
45
|
def self.assert(*args, &block)
|
43
46
|
options = args.last.kind_of?(Hash) ? args.pop : {}
|
44
47
|
args << options.merge(:strict => true)
|
45
48
|
Dependency::new(*args, &block).detect!
|
46
49
|
end
|
47
50
|
|
51
|
+
# Verify an absolute-path to an executable.
|
52
|
+
# @param (see Cliver::Dependency#initialize)
|
53
|
+
# EXCEPT: executable must be a single, absolute path.
|
54
|
+
# @option options [Boolean] :strict (true) @see Cliver::Dependency::initialize
|
55
|
+
# @raise (see Cliver::Dependency#detect!)
|
56
|
+
# @return (see Cliver::Dependency#detect!)
|
57
|
+
def self.verify!(executable, *args, &block)
|
58
|
+
unless File.absolute_path?(executable)
|
59
|
+
raise ArgumentError, "executable path must be absolute, " +
|
60
|
+
"got '#{executable.inspect}'."
|
61
|
+
end
|
62
|
+
Dependency::new(executable, *args, &block).detect!
|
63
|
+
end
|
64
|
+
|
48
65
|
extend self
|
49
66
|
|
50
67
|
# Wraps Cliver::assert and returns truthy/false instead of raising
|
data/lib/cliver/dependency.rb
CHANGED
@@ -55,8 +55,22 @@ module Cliver
|
|
55
55
|
@strict = options.fetch(:strict, false)
|
56
56
|
|
57
57
|
@executables = Array(executables).dup.freeze
|
58
|
-
|
59
58
|
@requirement = args unless args.empty?
|
59
|
+
|
60
|
+
check_compatibility!
|
61
|
+
end
|
62
|
+
|
63
|
+
# One of these things is not like the other ones...
|
64
|
+
# Some feature combinations just aren't compatible. This method ensures
|
65
|
+
# the the features selected for this object are compatible with each-other.
|
66
|
+
# @return [void]
|
67
|
+
# @raise [ArgumentError] if incompatibility found
|
68
|
+
def check_compatibility!
|
69
|
+
case
|
70
|
+
when @executables.any? {|exe| exe[File::SEPARATOR] && !File.absolute_path?(exe) }
|
71
|
+
# if the executable contains a path component, it *must* be absolute.
|
72
|
+
raise ArgumentError, "Relative-path executable requirements are not supported."
|
73
|
+
end
|
60
74
|
end
|
61
75
|
|
62
76
|
# Get all the installed versions of the api-compatible executables.
|
@@ -124,20 +138,18 @@ module Cliver
|
|
124
138
|
# @api private
|
125
139
|
# @raise [Cliver::Dependency::NotFound] with appropriate error message
|
126
140
|
def raise_not_found!
|
127
|
-
raise Dependency::NotFound.new
|
128
|
-
Could not find an executable #{@executables} on your path.
|
129
|
-
EOERR
|
141
|
+
raise Dependency::NotFound.new(
|
142
|
+
"Could not find an executable #{@executables} on your path.")
|
130
143
|
end
|
131
144
|
|
132
145
|
# @api private
|
133
146
|
# @raise [Cliver::Dependency::VersionMismatch] with appropriate error message
|
134
147
|
# @param installed [Hash<String,String>] the found versions
|
135
148
|
def raise_version_mismatch!(installed)
|
136
|
-
raise Dependency::VersionMismatch.new
|
137
|
-
Could not find an executable #{executable_description} that
|
138
|
-
requirements #{requirements_description}.
|
139
|
-
Found versions were #{installed.inspect}
|
140
|
-
EOERR
|
149
|
+
raise Dependency::VersionMismatch.new(
|
150
|
+
"Could not find an executable #{executable_description} that " +
|
151
|
+
"matched the requirements #{requirements_description}. " +
|
152
|
+
"Found versions were #{installed.inspect}.")
|
141
153
|
end
|
142
154
|
|
143
155
|
# @api private
|
@@ -189,9 +201,15 @@ module Cliver
|
|
189
201
|
paths = @path.sub('*', ENV['PATH']).split(File::PATH_SEPARATOR)
|
190
202
|
cmds = strict? ? @executables.first(1) : @executables
|
191
203
|
|
204
|
+
detected_exes = []
|
192
205
|
cmds.product(paths, exts).map do |cmd, path, ext|
|
193
|
-
exe = File.
|
194
|
-
|
206
|
+
exe = File.expand_path("#{cmd}#{ext}", path)
|
207
|
+
|
208
|
+
next unless File.executable?(exe)
|
209
|
+
next if detected_exes.include?(exe) # don't yield the same exe path 2x
|
210
|
+
|
211
|
+
detected_exes << exe
|
212
|
+
yield exe
|
195
213
|
end
|
196
214
|
end
|
197
215
|
end
|
data/lib/cliver/detector.rb
CHANGED
@@ -41,6 +41,13 @@ module Cliver
|
|
41
41
|
# version number.
|
42
42
|
def detect_version(executable_path)
|
43
43
|
output = shell_out_and_capture version_command(executable_path).shelljoin
|
44
|
+
if $?.exitstatus == 127
|
45
|
+
raise Cliver::Dependency::NotFound.new(
|
46
|
+
"Could not find an executable at given path '#{executable_path}'." +
|
47
|
+
"If this path was not specified explicitly, it is probably a " +
|
48
|
+
"bug in [Cliver](https://github.com/yaauie/cliver/issues)."
|
49
|
+
)
|
50
|
+
end
|
44
51
|
output[version_pattern]
|
45
52
|
end
|
46
53
|
|
data/lib/cliver/version.rb
CHANGED
@@ -0,0 +1,17 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
class File
|
4
|
+
# determine whether a String path is absolute.
|
5
|
+
# @example
|
6
|
+
# File.absolute_path?('foo') #=> false
|
7
|
+
# File.absolute_path?('/foo') #=> true
|
8
|
+
# File.absolute_path?('foo/bar') #=> false
|
9
|
+
# File.absolute_path?('/foo/bar') #=> true
|
10
|
+
# File.absolute_path?('C:foo/bar') #=> false
|
11
|
+
# File.absolute_path?('C:/foo/bar') #=> true
|
12
|
+
# @param [String] - a pathname
|
13
|
+
# @return [Boolean]
|
14
|
+
def self.absolute_path?(path)
|
15
|
+
false | File.dirname(path)[/\A([A-Z]:)?#{Regexp.escape(File::SEPARATOR)}/i]
|
16
|
+
end
|
17
|
+
end
|
data/spec/cliver_spec.rb
CHANGED
@@ -32,14 +32,14 @@ describe Cliver do
|
|
32
32
|
args << options
|
33
33
|
end
|
34
34
|
|
35
|
-
let(:path) { 'foo/bar
|
35
|
+
let(:path) { '/foo/bar:/baz/bingo' }
|
36
36
|
let(:executable) { 'doodle' }
|
37
37
|
let(:requirement) { '~>1.1'}
|
38
38
|
|
39
39
|
context 'when first-found version is sufficient' do
|
40
40
|
|
41
41
|
let(:version_map) do
|
42
|
-
{'baz/bingo/doodle' => '1.2.1'}
|
42
|
+
{'/baz/bingo/doodle' => '1.2.1'}
|
43
43
|
end
|
44
44
|
|
45
45
|
context '::assert' do
|
@@ -55,20 +55,94 @@ describe Cliver do
|
|
55
55
|
end
|
56
56
|
context '::detect' do
|
57
57
|
let(:method) { :detect }
|
58
|
-
it { should eq 'baz/bingo/doodle' }
|
58
|
+
it { should eq '/baz/bingo/doodle' }
|
59
59
|
end
|
60
60
|
context '::detect!' do
|
61
61
|
let(:method) { :detect! }
|
62
62
|
it 'should not raise' do
|
63
63
|
expect { action }.to_not raise_exception
|
64
64
|
end
|
65
|
-
it { should eq 'baz/bingo/doodle' }
|
65
|
+
it { should eq '/baz/bingo/doodle' }
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
context '::verify!' do
|
70
|
+
let(:method) { :verify! }
|
71
|
+
let(:version_map) do
|
72
|
+
{'/baz/bingo/doodle' => '0.2.1',
|
73
|
+
'/baz/fiddle/doodle' => '1.1.4'}
|
74
|
+
end
|
75
|
+
let(:args) do
|
76
|
+
args = [executable]
|
77
|
+
args.concat Array(requirement)
|
78
|
+
args << options
|
79
|
+
end
|
80
|
+
context 'when a relative path is given' do
|
81
|
+
let(:executable) { 'foo/bar/doodle' }
|
82
|
+
it 'should raise' do
|
83
|
+
expect { action }.to raise_exception ArgumentError
|
84
|
+
end
|
85
|
+
end
|
86
|
+
context 'when an absolute path is given' do
|
87
|
+
context 'and that path is not found' do
|
88
|
+
let(:executable) { '/blip/boom' }
|
89
|
+
it 'should raise' do
|
90
|
+
expect { action }.to raise_exception Cliver::Dependency::NotFound
|
91
|
+
end
|
92
|
+
end
|
93
|
+
context 'and the executable at that path is sufficent' do
|
94
|
+
let(:executable) { '/baz/fiddle/doodle' }
|
95
|
+
it 'should not raise' do
|
96
|
+
expect { action }.to_not raise_exception Cliver::Dependency::NotFound
|
97
|
+
end
|
98
|
+
end
|
99
|
+
context 'and the executable at that path is not sufficent' do
|
100
|
+
let(:executable) { '/baz/bingo/doodle' }
|
101
|
+
it 'should raise' do
|
102
|
+
expect { action }.to raise_exception Cliver::Dependency::VersionMismatch
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
context 'when given executable as a path' do
|
109
|
+
let(:version_map) do
|
110
|
+
{'/baz/bingo/doodle' => '1.2.1'}
|
111
|
+
end
|
112
|
+
let(:path) { '/fiddle/foo:/deedle/dee'}
|
113
|
+
|
114
|
+
context 'that is absolute' do
|
115
|
+
let(:executable) { '/baz/bingo/doodle' }
|
116
|
+
%w(assert dependency_unmet? detect detect).each do |method_name|
|
117
|
+
context "::#{method_name}" do
|
118
|
+
let(:method) { method_name.to_sym }
|
119
|
+
it 'should only detect its version once' do
|
120
|
+
Cliver::Dependency.any_instance.
|
121
|
+
should_receive(:detect_version).
|
122
|
+
once.
|
123
|
+
and_call_original
|
124
|
+
action
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
context 'that is relative' do
|
131
|
+
let(:executable) { 'baz/bingo/doodle' }
|
132
|
+
%w(assert dependency_unmet? detect detect).each do |method_name|
|
133
|
+
context "::#{method_name}" do
|
134
|
+
let(:method) { method_name.to_sym }
|
135
|
+
it 'should raise an ArgumentError' do
|
136
|
+
expect { action }.to raise_exception ArgumentError
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
66
140
|
end
|
67
141
|
end
|
68
142
|
|
69
143
|
context 'when first-found version insufficient' do
|
70
144
|
let(:version_map) do
|
71
|
-
{'baz/bingo/doodle' => '1.0.1'}
|
145
|
+
{'/baz/bingo/doodle' => '1.0.1'}
|
72
146
|
end
|
73
147
|
context '::assert' do
|
74
148
|
let(:method) { :assert }
|
@@ -94,8 +168,8 @@ describe Cliver do
|
|
94
168
|
context 'and when sufficient version found later on path' do
|
95
169
|
let(:version_map) do
|
96
170
|
{
|
97
|
-
'foo/bar/doodle' => '0.0.1',
|
98
|
-
'baz/bingo/doodle' => '1.1.0',
|
171
|
+
'/foo/bar/doodle' => '0.0.1',
|
172
|
+
'/baz/bingo/doodle' => '1.1.0',
|
99
173
|
}
|
100
174
|
end
|
101
175
|
context '::assert' do
|
@@ -110,14 +184,14 @@ describe Cliver do
|
|
110
184
|
end
|
111
185
|
context '::detect' do
|
112
186
|
let(:method) { :detect }
|
113
|
-
it { should eq 'baz/bingo/doodle'}
|
187
|
+
it { should eq '/baz/bingo/doodle'}
|
114
188
|
end
|
115
189
|
context '::detect!' do
|
116
190
|
let(:method) { :detect! }
|
117
191
|
it 'should not raise' do
|
118
192
|
expect { action }.to_not raise_exception
|
119
193
|
end
|
120
|
-
it { should eq 'baz/bingo/doodle' }
|
194
|
+
it { should eq '/baz/bingo/doodle' }
|
121
195
|
end
|
122
196
|
end
|
123
197
|
end
|
@@ -154,26 +228,26 @@ describe Cliver do
|
|
154
228
|
context 'and primary sufficient' do
|
155
229
|
let(:version_map) do
|
156
230
|
{
|
157
|
-
'baz/bingo/primary' => '1.1',
|
158
|
-
'foo/bar/fallback' => '1.1'
|
231
|
+
'/baz/bingo/primary' => '1.1',
|
232
|
+
'/foo/bar/fallback' => '1.1'
|
159
233
|
}
|
160
234
|
end
|
161
235
|
context '::detect' do
|
162
236
|
let(:method) { :detect }
|
163
|
-
it { should eq 'baz/bingo/primary' }
|
237
|
+
it { should eq '/baz/bingo/primary' }
|
164
238
|
end
|
165
239
|
end
|
166
240
|
context 'and primary insufficient' do
|
167
241
|
let(:version_map) do
|
168
242
|
{
|
169
|
-
'baz/bingo/primary' => '2.1',
|
170
|
-
'foo/bar/fallback' => '1.1'
|
243
|
+
'/baz/bingo/primary' => '2.1',
|
244
|
+
'/foo/bar/fallback' => '1.1'
|
171
245
|
}
|
172
246
|
end
|
173
247
|
context 'the secondary' do
|
174
248
|
context '::detect' do
|
175
249
|
let(:method) { :detect }
|
176
|
-
it { should eq 'foo/bar/fallback' }
|
250
|
+
it { should eq '/foo/bar/fallback' }
|
177
251
|
end
|
178
252
|
end
|
179
253
|
end
|
@@ -182,12 +256,12 @@ describe Cliver do
|
|
182
256
|
context 'and sufficient secondary does' do
|
183
257
|
let(:version_map) do
|
184
258
|
{
|
185
|
-
'foo/bar/fallback' => '1.1'
|
259
|
+
'/foo/bar/fallback' => '1.1'
|
186
260
|
}
|
187
261
|
end
|
188
262
|
context '::detect' do
|
189
263
|
let(:method) { :detect }
|
190
|
-
it { should eq 'foo/bar/fallback' }
|
264
|
+
it { should eq '/foo/bar/fallback' }
|
191
265
|
end
|
192
266
|
end
|
193
267
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: cliver
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.1
|
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-07-
|
12
|
+
date: 2013-07-27 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: bundler
|
@@ -128,6 +128,7 @@ files:
|
|
128
128
|
- lib/cliver/detector.rb
|
129
129
|
- lib/cliver/filter.rb
|
130
130
|
- lib/cliver/version.rb
|
131
|
+
- lib/core_ext/file.rb
|
131
132
|
- spec/cliver/detector_spec.rb
|
132
133
|
- spec/cliver_spec.rb
|
133
134
|
- spec/spec_helper.rb
|