cliver 0.2.0 → 0.2.1
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/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
|