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.
@@ -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#assert!)
41
- # @return (see Cliver::Dependency#assert!)
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
@@ -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 <<-EOERR
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 <<-EOERR
137
- Could not find an executable #{executable_description} that matched the
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.join(path, "#{cmd}#{ext}")
194
- yield exe if File.executable?(exe)
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
@@ -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
 
@@ -2,5 +2,5 @@
2
2
 
3
3
  module Cliver
4
4
  # Cliver follows {http://semver.org SemVer}
5
- VERSION = '0.2.0'
5
+ VERSION = '0.2.1'
6
6
  end
@@ -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
@@ -32,14 +32,14 @@ describe Cliver do
32
32
  args << options
33
33
  end
34
34
 
35
- let(:path) { 'foo/bar:baz/bingo' }
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.0
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-08 00:00:00.000000000 Z
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