vim-flavor 0.0.2 → 0.0.3
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/README.md +44 -0
- data/lib/vim-flavor.rb +11 -462
- data/lib/vim-flavor/cli.rb +29 -0
- data/lib/vim-flavor/facade.rb +156 -0
- data/lib/vim-flavor/flavor.rb +119 -0
- data/lib/vim-flavor/flavorfile.rb +62 -0
- data/lib/vim-flavor/lockfile.rb +64 -0
- data/lib/vim-flavor/stringextension.rb +9 -0
- data/lib/vim-flavor/version.rb +1 -1
- data/lib/vim-flavor/versionconstraint.rb +48 -0
- data/spec/facade_spec.rb +1 -1
- data/spec/flavor_spec.rb +6 -6
- metadata +9 -2
data/README.md
CHANGED
@@ -28,6 +28,50 @@
|
|
28
28
|
gem install vim-flavor
|
29
29
|
|
30
30
|
|
31
|
+
### Installable plugins
|
32
|
+
|
33
|
+
Not all Vim plugins can be installed with vim-flavor.
|
34
|
+
vim-flavor can install plugins which meet the following conditions:
|
35
|
+
|
36
|
+
* Plugins must have dedicated Git repositories.
|
37
|
+
vim-flavor does not support other version control systems.
|
38
|
+
This is an intentional design. Because:
|
39
|
+
* [vim-scripts.org](http://vim-scripts.org/) provides
|
40
|
+
[a comprehensive Git mirrors](https://github.com/vim-scripts) for
|
41
|
+
[plugins uploaded to www.vim.org](http://www.vim.org/scripts/index.php).
|
42
|
+
* Experimental plugins which are not uploaded to www.vim.org
|
43
|
+
are usually found in [GitHub](https://github.com/).
|
44
|
+
* Plugins must follow [the versioning pocilies of
|
45
|
+
RubyGems](http://docs.rubygems.org/read/chapter/7#page26) and have "version"
|
46
|
+
tags in their repositories. For example, if there is the version 1.2.3 of
|
47
|
+
a plugin, its repository must have the tag `1.2.3`, and the files of the
|
48
|
+
version 1.2.3 can be checked out via the tag `1.2.3`. In other words,
|
49
|
+
plugins which do not have proper tags are not installabe.
|
50
|
+
This is an intentional design. Because:
|
51
|
+
* It's not possible to determine whether two versions are compatible or not
|
52
|
+
without "version" tags. Compatibility is a big problem to resolve
|
53
|
+
dependencies of plugins. For example, if plugin A requires plugin X 1.2.3
|
54
|
+
or later while plugin B requires plugin X 2.0 or later, it's not possible
|
55
|
+
to use A and B at the same time. Such problems should be detected before
|
56
|
+
installing plugins.
|
57
|
+
* Git mirrors by vim-scripts.org are tagged with version numbers.
|
58
|
+
* Some Git repositories might not have "version" tags.
|
59
|
+
Such plugins are not ready to use for everyone.
|
60
|
+
So that it should not be installable.
|
61
|
+
* Plugins must have proper directory structures.
|
62
|
+
For example, directories such as `autoload`, `syntax` etc should exist in
|
63
|
+
the roots of plugins.
|
64
|
+
This is an intentional design. Because:
|
65
|
+
* Git mirrors by vim-scripts.org have proper directory structures even if
|
66
|
+
the original plugins are uploaded to www.vim.org without proper directory
|
67
|
+
structures. (A good example is
|
68
|
+
[a.vim](http://www.vim.org/scripts/script.php?script_id=31) and
|
69
|
+
[its mirror](https://github.com/vim-scripts/a.vim).)
|
70
|
+
* Other Git repositoris might not have proper directory structures.
|
71
|
+
Such plugins are not ready to use for everyone.
|
72
|
+
So that it should not be installable.
|
73
|
+
|
74
|
+
|
31
75
|
|
32
76
|
|
33
77
|
## Typical usage
|
data/lib/vim-flavor.rb
CHANGED
@@ -1,66 +1,24 @@
|
|
1
1
|
require 'bundler/setup'
|
2
|
-
require 'fileutils'
|
3
|
-
require 'thor'
|
4
|
-
require 'vim-flavor/version'
|
5
|
-
require 'yaml'
|
6
2
|
|
7
3
|
module Vim
|
8
4
|
module Flavor
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
5
|
+
[
|
6
|
+
:CLI,
|
7
|
+
:Facade,
|
8
|
+
:Flavor,
|
9
|
+
:FlavorFile,
|
10
|
+
:LockFile,
|
11
|
+
:StringExtension,
|
12
|
+
:VERSION,
|
13
|
+
:VersionConstraint,
|
14
|
+
].each do |name|
|
15
|
+
autoload name, "vim-flavor/#{name.to_s().downcase()}"
|
13
16
|
end
|
14
17
|
|
15
18
|
class ::String
|
16
19
|
include StringExtension
|
17
20
|
end
|
18
21
|
|
19
|
-
class VersionConstraint
|
20
|
-
attr_reader :base_version, :operator
|
21
|
-
|
22
|
-
def initialize(s)
|
23
|
-
@base_version, @operator = parse(s)
|
24
|
-
end
|
25
|
-
|
26
|
-
def to_s()
|
27
|
-
"#{@operator} #{@base_version}"
|
28
|
-
end
|
29
|
-
|
30
|
-
def ==(other)
|
31
|
-
self.base_version == other.base_version &&
|
32
|
-
self.operator == other.operator
|
33
|
-
end
|
34
|
-
|
35
|
-
def parse(s)
|
36
|
-
m = /^\s*(>=|~>)\s+(\S+)$/.match(s)
|
37
|
-
if m then
|
38
|
-
[Gem::Version.create(m[2]), m[1]]
|
39
|
-
else
|
40
|
-
raise "Invalid version constraint: #{s.inspect}"
|
41
|
-
end
|
42
|
-
end
|
43
|
-
|
44
|
-
def compatible?(other_version_or_s)
|
45
|
-
v = Gem::Version.create(other_version_or_s)
|
46
|
-
if @operator == '~>' then
|
47
|
-
self.base_version.bump() > v and v >= self.base_version
|
48
|
-
elsif @operator == '>=' then
|
49
|
-
v >= self.base_version
|
50
|
-
else
|
51
|
-
raise NotImplementedError
|
52
|
-
end
|
53
|
-
end
|
54
|
-
|
55
|
-
def find_the_best_version(versions)
|
56
|
-
versions.
|
57
|
-
select {|v| compatible?(v)}.
|
58
|
-
sort().
|
59
|
-
reverse().
|
60
|
-
first
|
61
|
-
end
|
62
|
-
end
|
63
|
-
|
64
22
|
class << self
|
65
23
|
@@dot_path = File.expand_path('~/.vim-flavor')
|
66
24
|
|
@@ -72,414 +30,5 @@ module Vim
|
|
72
30
|
@@dot_path = path
|
73
31
|
end
|
74
32
|
end
|
75
|
-
|
76
|
-
class Flavor
|
77
|
-
@@properties = [
|
78
|
-
:groups,
|
79
|
-
:locked_version,
|
80
|
-
:repo_name,
|
81
|
-
:repo_uri,
|
82
|
-
:version_contraint,
|
83
|
-
]
|
84
|
-
|
85
|
-
@@properties.each do |p|
|
86
|
-
attr_accessor p
|
87
|
-
end
|
88
|
-
|
89
|
-
def initialize()
|
90
|
-
@groups = []
|
91
|
-
end
|
92
|
-
|
93
|
-
def ==(other)
|
94
|
-
return false if self.class != other.class
|
95
|
-
@@properties.all? do |p|
|
96
|
-
self.send(p) == other.send(p)
|
97
|
-
end
|
98
|
-
end
|
99
|
-
|
100
|
-
def zapped_repo_dir_name
|
101
|
-
@repo_name.gsub(/[^A-Za-z0-9._-]/, '_')
|
102
|
-
end
|
103
|
-
|
104
|
-
def cached_repo_path
|
105
|
-
@cached_repo_path ||=
|
106
|
-
"#{Vim::Flavor.dot_path}/repos/#{zapped_repo_dir_name}"
|
107
|
-
end
|
108
|
-
|
109
|
-
def make_deploy_path(vimfiles_path)
|
110
|
-
"#{vimfiles_path.to_flavors_path()}/#{zapped_repo_dir_name}"
|
111
|
-
end
|
112
|
-
|
113
|
-
def clone()
|
114
|
-
message = %x[
|
115
|
-
{
|
116
|
-
git clone '#{@repo_uri}' '#{cached_repo_path}'
|
117
|
-
} 2>&1
|
118
|
-
]
|
119
|
-
if $? != 0 then
|
120
|
-
raise RuntimeError, message
|
121
|
-
end
|
122
|
-
true
|
123
|
-
end
|
124
|
-
|
125
|
-
def fetch()
|
126
|
-
message = %x[
|
127
|
-
{
|
128
|
-
cd #{cached_repo_path.inspect} &&
|
129
|
-
git fetch origin
|
130
|
-
} 2>&1
|
131
|
-
]
|
132
|
-
if $? != 0 then
|
133
|
-
raise RuntimeError, message
|
134
|
-
end
|
135
|
-
end
|
136
|
-
|
137
|
-
def deploy(vimfiles_path)
|
138
|
-
deploy_path = make_deploy_path(vimfiles_path)
|
139
|
-
message = %x[
|
140
|
-
{
|
141
|
-
cd '#{cached_repo_path}' &&
|
142
|
-
git checkout -f #{locked_version.inspect} &&
|
143
|
-
git checkout-index -a -f --prefix='#{deploy_path}/' &&
|
144
|
-
{
|
145
|
-
vim -u NONE -i NONE -n -N -e -s -c '
|
146
|
-
silent! helptags #{deploy_path}/doc
|
147
|
-
qall!
|
148
|
-
' || true
|
149
|
-
}
|
150
|
-
} 2>&1
|
151
|
-
]
|
152
|
-
if $? != 0 then
|
153
|
-
raise RuntimeError, message
|
154
|
-
end
|
155
|
-
end
|
156
|
-
|
157
|
-
def undeploy(vimfiles_path)
|
158
|
-
deploy_path = make_deploy_path(vimfiles_path)
|
159
|
-
message = %x[
|
160
|
-
{
|
161
|
-
rm -fr '#{deploy_path}'
|
162
|
-
} 2>&1
|
163
|
-
]
|
164
|
-
if $? != 0 then
|
165
|
-
raise RuntimeError, message
|
166
|
-
end
|
167
|
-
end
|
168
|
-
|
169
|
-
def list_versions()
|
170
|
-
tags = %x[
|
171
|
-
{
|
172
|
-
cd '#{cached_repo_path}' &&
|
173
|
-
git tag
|
174
|
-
} 2>&1
|
175
|
-
]
|
176
|
-
if $? != 0 then
|
177
|
-
raise RuntimeError, message
|
178
|
-
end
|
179
|
-
|
180
|
-
tags.
|
181
|
-
split(/[\r\n]/).
|
182
|
-
select {|t| t != ''}.
|
183
|
-
map {|t| Gem::Version.create(t)}
|
184
|
-
end
|
185
|
-
|
186
|
-
def update_locked_version()
|
187
|
-
@locked_version =
|
188
|
-
version_contraint.find_the_best_version(list_versions())
|
189
|
-
end
|
190
|
-
end
|
191
|
-
|
192
|
-
class FlavorFile
|
193
|
-
attr_reader :flavors
|
194
|
-
|
195
|
-
def initialize()
|
196
|
-
@flavors = {}
|
197
|
-
@default_groups = [:default]
|
198
|
-
end
|
199
|
-
|
200
|
-
def interpret(&block)
|
201
|
-
instance_eval(&block)
|
202
|
-
end
|
203
|
-
|
204
|
-
def eval_flavorfile(flavorfile_path)
|
205
|
-
content = File.open(flavorfile_path, 'rb') do |f|
|
206
|
-
f.read()
|
207
|
-
end
|
208
|
-
interpret do
|
209
|
-
instance_eval(content)
|
210
|
-
end
|
211
|
-
end
|
212
|
-
|
213
|
-
def repo_uri_from_repo_name(repo_name)
|
214
|
-
if /^([^\/]+)$/.match(repo_name) then
|
215
|
-
m = Regexp.last_match
|
216
|
-
"git://github.com/vim-scripts/#{m[1]}.git"
|
217
|
-
elsif /^([A-Za-z0-9_-]+)\/(.*)$/.match(repo_name) then
|
218
|
-
m = Regexp.last_match
|
219
|
-
"git://github.com/#{m[1]}/#{m[2]}.git"
|
220
|
-
elsif /^[a-z]+:\/\/.*$/.match(repo_name) then
|
221
|
-
repo_name
|
222
|
-
else
|
223
|
-
raise "repo_name is written in invalid format: #{repo_name.inspect}"
|
224
|
-
end
|
225
|
-
end
|
226
|
-
|
227
|
-
def flavor(repo_name, *args)
|
228
|
-
options = Hash === args.last ? args.pop : {}
|
229
|
-
options[:groups] ||= []
|
230
|
-
version_contraint = VersionConstraint.new(args.last || '>= 0')
|
231
|
-
|
232
|
-
f = Flavor.new()
|
233
|
-
f.repo_name = repo_name
|
234
|
-
f.repo_uri = repo_uri_from_repo_name(repo_name)
|
235
|
-
f.version_contraint = version_contraint
|
236
|
-
f.groups = @default_groups + options[:groups]
|
237
|
-
|
238
|
-
@flavors[f.repo_uri] = f
|
239
|
-
end
|
240
|
-
|
241
|
-
def group(*group_names, &block)
|
242
|
-
@default_groups.concat(group_names)
|
243
|
-
yield
|
244
|
-
ensure
|
245
|
-
group_names.each do
|
246
|
-
@default_groups.pop()
|
247
|
-
end
|
248
|
-
end
|
249
|
-
end
|
250
|
-
|
251
|
-
class LockFile
|
252
|
-
# TODO: Resolve dependencies recursively.
|
253
|
-
|
254
|
-
attr_reader :flavors, :path
|
255
|
-
|
256
|
-
def initialize(path)
|
257
|
-
@flavors = {} # repo_uri => flavor
|
258
|
-
@path = path
|
259
|
-
end
|
260
|
-
|
261
|
-
def load()
|
262
|
-
h = File.open(@path, 'rb') do |f|
|
263
|
-
YAML.load(f.read())
|
264
|
-
end
|
265
|
-
|
266
|
-
@flavors = self.class.flavors_from_poro(h[:flavors])
|
267
|
-
end
|
268
|
-
|
269
|
-
def save()
|
270
|
-
h = {}
|
271
|
-
|
272
|
-
h[:flavors] = self.class.poro_from_flavors(@flavors)
|
273
|
-
|
274
|
-
File.open(@path, 'wb') do |f|
|
275
|
-
YAML.dump(h, f)
|
276
|
-
end
|
277
|
-
end
|
278
|
-
|
279
|
-
def self.poro_from_flavors(flavors)
|
280
|
-
Hash[
|
281
|
-
flavors.values.map {|f|
|
282
|
-
[
|
283
|
-
f.repo_uri,
|
284
|
-
{
|
285
|
-
:groups => f.groups,
|
286
|
-
:locked_version => f.locked_version.to_s(),
|
287
|
-
:repo_name => f.repo_name,
|
288
|
-
:version_contraint => f.version_contraint.to_s(),
|
289
|
-
}
|
290
|
-
]
|
291
|
-
}
|
292
|
-
]
|
293
|
-
end
|
294
|
-
|
295
|
-
def self.flavors_from_poro(poro)
|
296
|
-
Hash[
|
297
|
-
poro.to_a().map {|repo_uri, h|
|
298
|
-
f = Flavor.new()
|
299
|
-
f.groups = h[:groups]
|
300
|
-
f.locked_version = Gem::Version.create(h[:locked_version])
|
301
|
-
f.repo_name = h[:repo_name]
|
302
|
-
f.repo_uri = repo_uri
|
303
|
-
f.version_contraint = VersionConstraint.new(h[:version_contraint])
|
304
|
-
[f.repo_uri, f]
|
305
|
-
}
|
306
|
-
]
|
307
|
-
end
|
308
|
-
end
|
309
|
-
|
310
|
-
class Facade
|
311
|
-
attr_reader :flavorfile
|
312
|
-
attr_accessor :flavorfile_path
|
313
|
-
attr_reader :lockfile
|
314
|
-
attr_accessor :lockfile_path
|
315
|
-
attr_accessor :traced
|
316
|
-
|
317
|
-
def initialize()
|
318
|
-
@flavorfile = nil # FlavorFile
|
319
|
-
@flavorfile_path = "#{Dir.getwd()}/VimFlavor"
|
320
|
-
@lockfile = nil # LockFile
|
321
|
-
@lockfile_path = "#{Dir.getwd()}/VimFlavor.lock"
|
322
|
-
@traced = false
|
323
|
-
end
|
324
|
-
|
325
|
-
def trace(message)
|
326
|
-
print(message) if @traced
|
327
|
-
end
|
328
|
-
|
329
|
-
def load()
|
330
|
-
@flavorfile = FlavorFile.new()
|
331
|
-
@flavorfile.eval_flavorfile(@flavorfile_path)
|
332
|
-
|
333
|
-
@lockfile = LockFile.new(@lockfile_path)
|
334
|
-
@lockfile.load() if File.exists?(@lockfile_path)
|
335
|
-
end
|
336
|
-
|
337
|
-
def make_new_flavors(current_flavors, locked_flavors, mode)
|
338
|
-
new_flavors = {}
|
339
|
-
|
340
|
-
current_flavors.each do |repo_uri, cf|
|
341
|
-
lf = locked_flavors[repo_uri]
|
342
|
-
nf = cf.dup()
|
343
|
-
|
344
|
-
nf.locked_version =
|
345
|
-
if (not lf) or
|
346
|
-
cf.version_contraint != lf.version_contraint or
|
347
|
-
mode == :update then
|
348
|
-
cf.locked_version
|
349
|
-
else
|
350
|
-
lf.locked_version
|
351
|
-
end
|
352
|
-
|
353
|
-
new_flavors[repo_uri] = nf
|
354
|
-
end
|
355
|
-
|
356
|
-
new_flavors
|
357
|
-
end
|
358
|
-
|
359
|
-
def create_vim_script_for_bootstrap(vimfiles_path)
|
360
|
-
bootstrap_path = "#{vimfiles_path.to_flavors_path()}/bootstrap.vim"
|
361
|
-
FileUtils.mkdir_p(File.dirname(bootstrap_path))
|
362
|
-
File.open(bootstrap_path, 'w') do |f|
|
363
|
-
f.write(<<-'END')
|
364
|
-
function! s:bootstrap()
|
365
|
-
let current_rtp = &runtimepath
|
366
|
-
let current_rtps = split(current_rtp, ',')
|
367
|
-
set runtimepath&
|
368
|
-
let default_rtp = &runtimepath
|
369
|
-
let default_rtps = split(default_rtp, ',')
|
370
|
-
let user_dir = default_rtps[0]
|
371
|
-
let user_after_dir = default_rtps[-1]
|
372
|
-
let base_rtps =
|
373
|
-
\ filter(copy(current_rtps),
|
374
|
-
\ 'v:val !=# user_dir && v:val !=# user_after_dir')
|
375
|
-
let flavor_dirs =
|
376
|
-
\ filter(split(glob(user_dir . '/flavors/*'), '\n'),
|
377
|
-
\ 'isdirectory(v:val)')
|
378
|
-
let new_rtps =
|
379
|
-
\ []
|
380
|
-
\ + [user_dir]
|
381
|
-
\ + flavor_dirs
|
382
|
-
\ + base_rtps
|
383
|
-
\ + map(reverse(copy(flavor_dirs)), 'v:val . "/after"')
|
384
|
-
\ + [user_after_dir]
|
385
|
-
let &runtimepath = join(new_rtps, ',')
|
386
|
-
endfunction
|
387
|
-
|
388
|
-
call s:bootstrap()
|
389
|
-
END
|
390
|
-
end
|
391
|
-
end
|
392
|
-
|
393
|
-
def deploy_flavors(flavor_list, vimfiles_path)
|
394
|
-
FileUtils.rm_rf(
|
395
|
-
["#{vimfiles_path.to_flavors_path()}"],
|
396
|
-
:secure => true
|
397
|
-
)
|
398
|
-
|
399
|
-
create_vim_script_for_bootstrap(vimfiles_path)
|
400
|
-
flavor_list.each do |f|
|
401
|
-
trace("Deploying #{f.repo_name} (#{f.locked_version})\n")
|
402
|
-
f.deploy(vimfiles_path)
|
403
|
-
end
|
404
|
-
end
|
405
|
-
|
406
|
-
def save_lockfile()
|
407
|
-
@lockfile.save()
|
408
|
-
end
|
409
|
-
|
410
|
-
def complete_locked_flavors(mode)
|
411
|
-
nfs = {}
|
412
|
-
@flavorfile.flavors.each do |repo_uri, cf|
|
413
|
-
nf = cf.dup()
|
414
|
-
lf = @lockfile.flavors[repo_uri]
|
415
|
-
|
416
|
-
trace("Using #{nf.repo_name} ... ")
|
417
|
-
begin
|
418
|
-
if not File.exists?(nf.cached_repo_path)
|
419
|
-
nf.clone()
|
420
|
-
end
|
421
|
-
|
422
|
-
if mode == :upgrade_all or
|
423
|
-
(not lf) or
|
424
|
-
nf.version_contraint != lf.version_contraint then
|
425
|
-
nf.fetch()
|
426
|
-
nf.update_locked_version()
|
427
|
-
else
|
428
|
-
nf.locked_version = lf.locked_version
|
429
|
-
end
|
430
|
-
end
|
431
|
-
trace("(#{nf.locked_version})\n")
|
432
|
-
|
433
|
-
nfs[repo_uri] = nf
|
434
|
-
end
|
435
|
-
|
436
|
-
@lockfile.instance_eval do
|
437
|
-
@flavors = nfs
|
438
|
-
end
|
439
|
-
end
|
440
|
-
|
441
|
-
def get_default_vimfiles_path()
|
442
|
-
# FIXME: Compute more appropriate value.
|
443
|
-
"#{ENV['HOME']}/.vim"
|
444
|
-
end
|
445
|
-
|
446
|
-
def install(vimfiles_path)
|
447
|
-
load()
|
448
|
-
complete_locked_flavors(:upgrade_if_necessary)
|
449
|
-
save_lockfile()
|
450
|
-
deploy_flavors(lockfile.flavors.values, vimfiles_path)
|
451
|
-
end
|
452
|
-
|
453
|
-
def upgrade(vimfiles_path)
|
454
|
-
load()
|
455
|
-
complete_locked_flavors(:upgrade_all)
|
456
|
-
save_lockfile()
|
457
|
-
deploy_flavors(lockfile.flavors.values, vimfiles_path)
|
458
|
-
end
|
459
|
-
end
|
460
|
-
|
461
|
-
class CLI < Thor
|
462
|
-
desc 'install', 'Install Vim plugins according to VimFlavor file.'
|
463
|
-
method_option :vimfiles_path,
|
464
|
-
:desc => 'A path to your vimfiles directory.'
|
465
|
-
def install()
|
466
|
-
facade = Facade.new()
|
467
|
-
facade.traced = true
|
468
|
-
facade.install(
|
469
|
-
options[:vimfiles_path] || facade.get_default_vimfiles_path()
|
470
|
-
)
|
471
|
-
end
|
472
|
-
|
473
|
-
desc 'upgrade', 'Upgrade Vim plugins according to VimFlavor file.'
|
474
|
-
method_option :vimfiles_path,
|
475
|
-
:desc => 'A path to your vimfiles directory.'
|
476
|
-
def upgrade()
|
477
|
-
facade = Facade.new()
|
478
|
-
facade.traced = true
|
479
|
-
facade.upgrade(
|
480
|
-
options[:vimfiles_path] || facade.get_default_vimfiles_path()
|
481
|
-
)
|
482
|
-
end
|
483
|
-
end
|
484
33
|
end
|
485
34
|
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'thor'
|
2
|
+
|
3
|
+
module Vim
|
4
|
+
module Flavor
|
5
|
+
class CLI < Thor
|
6
|
+
desc 'install', 'Install Vim plugins according to VimFlavor file.'
|
7
|
+
method_option :vimfiles_path,
|
8
|
+
:desc => 'A path to your vimfiles directory.'
|
9
|
+
def install()
|
10
|
+
facade = Facade.new()
|
11
|
+
facade.traced = true
|
12
|
+
facade.install(
|
13
|
+
options[:vimfiles_path] || facade.get_default_vimfiles_path()
|
14
|
+
)
|
15
|
+
end
|
16
|
+
|
17
|
+
desc 'upgrade', 'Upgrade Vim plugins according to VimFlavor file.'
|
18
|
+
method_option :vimfiles_path,
|
19
|
+
:desc => 'A path to your vimfiles directory.'
|
20
|
+
def upgrade()
|
21
|
+
facade = Facade.new()
|
22
|
+
facade.traced = true
|
23
|
+
facade.upgrade(
|
24
|
+
options[:vimfiles_path] || facade.get_default_vimfiles_path()
|
25
|
+
)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,156 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
|
3
|
+
module Vim
|
4
|
+
module Flavor
|
5
|
+
class Facade
|
6
|
+
attr_reader :flavorfile
|
7
|
+
attr_accessor :flavorfile_path
|
8
|
+
attr_reader :lockfile
|
9
|
+
attr_accessor :lockfile_path
|
10
|
+
attr_accessor :traced
|
11
|
+
|
12
|
+
def initialize()
|
13
|
+
@flavorfile = nil # FlavorFile
|
14
|
+
@flavorfile_path = "#{Dir.getwd()}/VimFlavor"
|
15
|
+
@lockfile = nil # LockFile
|
16
|
+
@lockfile_path = "#{Dir.getwd()}/VimFlavor.lock"
|
17
|
+
@traced = false
|
18
|
+
end
|
19
|
+
|
20
|
+
def trace(message)
|
21
|
+
print(message) if @traced
|
22
|
+
end
|
23
|
+
|
24
|
+
def load()
|
25
|
+
@flavorfile = FlavorFile.new()
|
26
|
+
@flavorfile.eval_flavorfile(@flavorfile_path)
|
27
|
+
|
28
|
+
@lockfile = LockFile.new(@lockfile_path)
|
29
|
+
@lockfile.load() if File.exists?(@lockfile_path)
|
30
|
+
end
|
31
|
+
|
32
|
+
def make_new_flavors(current_flavors, locked_flavors, mode)
|
33
|
+
new_flavors = {}
|
34
|
+
|
35
|
+
current_flavors.each do |repo_uri, cf|
|
36
|
+
lf = locked_flavors[repo_uri]
|
37
|
+
nf = cf.dup()
|
38
|
+
|
39
|
+
nf.locked_version =
|
40
|
+
if (not lf) or
|
41
|
+
cf.version_contraint != lf.version_contraint or
|
42
|
+
mode == :update then
|
43
|
+
cf.locked_version
|
44
|
+
else
|
45
|
+
lf.locked_version
|
46
|
+
end
|
47
|
+
|
48
|
+
new_flavors[repo_uri] = nf
|
49
|
+
end
|
50
|
+
|
51
|
+
new_flavors
|
52
|
+
end
|
53
|
+
|
54
|
+
def create_vim_script_for_bootstrap(vimfiles_path)
|
55
|
+
bootstrap_path = "#{vimfiles_path.to_flavors_path()}/bootstrap.vim"
|
56
|
+
FileUtils.mkdir_p(File.dirname(bootstrap_path))
|
57
|
+
File.open(bootstrap_path, 'w') do |f|
|
58
|
+
f.write(<<-'END')
|
59
|
+
function! s:bootstrap()
|
60
|
+
let current_rtp = &runtimepath
|
61
|
+
let current_rtps = split(current_rtp, ',')
|
62
|
+
set runtimepath&
|
63
|
+
let default_rtp = &runtimepath
|
64
|
+
let default_rtps = split(default_rtp, ',')
|
65
|
+
let user_dir = default_rtps[0]
|
66
|
+
let user_after_dir = default_rtps[-1]
|
67
|
+
let base_rtps =
|
68
|
+
\ filter(copy(current_rtps),
|
69
|
+
\ 'v:val !=# user_dir && v:val !=# user_after_dir')
|
70
|
+
let flavor_dirs =
|
71
|
+
\ filter(split(glob(user_dir . '/flavors/*'), '\n'),
|
72
|
+
\ 'isdirectory(v:val)')
|
73
|
+
let new_rtps =
|
74
|
+
\ []
|
75
|
+
\ + [user_dir]
|
76
|
+
\ + flavor_dirs
|
77
|
+
\ + base_rtps
|
78
|
+
\ + map(reverse(copy(flavor_dirs)), 'v:val . "/after"')
|
79
|
+
\ + [user_after_dir]
|
80
|
+
let &runtimepath = join(new_rtps, ',')
|
81
|
+
endfunction
|
82
|
+
|
83
|
+
call s:bootstrap()
|
84
|
+
END
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def deploy_flavors(flavor_list, vimfiles_path)
|
89
|
+
FileUtils.rm_rf(
|
90
|
+
["#{vimfiles_path.to_flavors_path()}"],
|
91
|
+
:secure => true
|
92
|
+
)
|
93
|
+
|
94
|
+
create_vim_script_for_bootstrap(vimfiles_path)
|
95
|
+
flavor_list.each do |f|
|
96
|
+
trace("Deploying #{f.repo_name} (#{f.locked_version})\n")
|
97
|
+
f.deploy(vimfiles_path)
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
def save_lockfile()
|
102
|
+
@lockfile.save()
|
103
|
+
end
|
104
|
+
|
105
|
+
def complete_locked_flavors(mode)
|
106
|
+
nfs = {}
|
107
|
+
@flavorfile.flavors.each do |repo_uri, cf|
|
108
|
+
nf = cf.dup()
|
109
|
+
lf = @lockfile.flavors[repo_uri]
|
110
|
+
|
111
|
+
trace("Using #{nf.repo_name} ... ")
|
112
|
+
begin
|
113
|
+
if not File.exists?(nf.cached_repo_path)
|
114
|
+
nf.clone()
|
115
|
+
end
|
116
|
+
|
117
|
+
if mode == :upgrade_all or
|
118
|
+
(not lf) or
|
119
|
+
nf.version_contraint != lf.version_contraint then
|
120
|
+
nf.fetch()
|
121
|
+
nf.update_locked_version()
|
122
|
+
else
|
123
|
+
nf.locked_version = lf.locked_version
|
124
|
+
end
|
125
|
+
end
|
126
|
+
trace("(#{nf.locked_version})\n")
|
127
|
+
|
128
|
+
nfs[repo_uri] = nf
|
129
|
+
end
|
130
|
+
|
131
|
+
@lockfile.instance_eval do
|
132
|
+
@flavors = nfs
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
def get_default_vimfiles_path()
|
137
|
+
# FIXME: Compute more appropriate value.
|
138
|
+
"#{ENV['HOME']}/.vim"
|
139
|
+
end
|
140
|
+
|
141
|
+
def install(vimfiles_path)
|
142
|
+
load()
|
143
|
+
complete_locked_flavors(:upgrade_if_necessary)
|
144
|
+
save_lockfile()
|
145
|
+
deploy_flavors(lockfile.flavors.values, vimfiles_path)
|
146
|
+
end
|
147
|
+
|
148
|
+
def upgrade(vimfiles_path)
|
149
|
+
load()
|
150
|
+
complete_locked_flavors(:upgrade_all)
|
151
|
+
save_lockfile()
|
152
|
+
deploy_flavors(lockfile.flavors.values, vimfiles_path)
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
@@ -0,0 +1,119 @@
|
|
1
|
+
module Vim
|
2
|
+
module Flavor
|
3
|
+
class Flavor
|
4
|
+
@@properties = [
|
5
|
+
:groups,
|
6
|
+
:locked_version,
|
7
|
+
:repo_name,
|
8
|
+
:repo_uri,
|
9
|
+
:version_contraint,
|
10
|
+
]
|
11
|
+
|
12
|
+
@@properties.each do |p|
|
13
|
+
attr_accessor p
|
14
|
+
end
|
15
|
+
|
16
|
+
def initialize()
|
17
|
+
@groups = []
|
18
|
+
end
|
19
|
+
|
20
|
+
def ==(other)
|
21
|
+
return false if self.class != other.class
|
22
|
+
@@properties.all? do |p|
|
23
|
+
self.send(p) == other.send(p)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def zapped_repo_dir_name
|
28
|
+
@repo_name.gsub(/[^A-Za-z0-9._-]/, '_')
|
29
|
+
end
|
30
|
+
|
31
|
+
def cached_repo_path
|
32
|
+
@cached_repo_path ||=
|
33
|
+
"#{Vim::Flavor.dot_path}/repos/#{zapped_repo_dir_name}"
|
34
|
+
end
|
35
|
+
|
36
|
+
def make_deploy_path(vimfiles_path)
|
37
|
+
"#{vimfiles_path.to_flavors_path()}/#{zapped_repo_dir_name}"
|
38
|
+
end
|
39
|
+
|
40
|
+
def clone()
|
41
|
+
message = %x[
|
42
|
+
{
|
43
|
+
git clone '#{@repo_uri}' '#{cached_repo_path}'
|
44
|
+
} 2>&1
|
45
|
+
]
|
46
|
+
if $? != 0 then
|
47
|
+
raise RuntimeError, message
|
48
|
+
end
|
49
|
+
true
|
50
|
+
end
|
51
|
+
|
52
|
+
def fetch()
|
53
|
+
message = %x[
|
54
|
+
{
|
55
|
+
cd #{cached_repo_path.inspect} &&
|
56
|
+
git fetch origin
|
57
|
+
} 2>&1
|
58
|
+
]
|
59
|
+
if $? != 0 then
|
60
|
+
raise RuntimeError, message
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def deploy(vimfiles_path)
|
65
|
+
deploy_path = make_deploy_path(vimfiles_path)
|
66
|
+
message = %x[
|
67
|
+
{
|
68
|
+
cd '#{cached_repo_path}' &&
|
69
|
+
git checkout -f '#{locked_version}' &&
|
70
|
+
git checkout-index -a -f --prefix='#{deploy_path}/' &&
|
71
|
+
{
|
72
|
+
vim -u NONE -i NONE -n -N -e -s -c '
|
73
|
+
silent! helptags #{deploy_path}/doc
|
74
|
+
qall!
|
75
|
+
' || true
|
76
|
+
}
|
77
|
+
} 2>&1
|
78
|
+
]
|
79
|
+
if $? != 0 then
|
80
|
+
raise RuntimeError, message
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def undeploy(vimfiles_path)
|
85
|
+
deploy_path = make_deploy_path(vimfiles_path)
|
86
|
+
message = %x[
|
87
|
+
{
|
88
|
+
rm -fr '#{deploy_path}'
|
89
|
+
} 2>&1
|
90
|
+
]
|
91
|
+
if $? != 0 then
|
92
|
+
raise RuntimeError, message
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def list_versions()
|
97
|
+
tags = %x[
|
98
|
+
{
|
99
|
+
cd '#{cached_repo_path}' &&
|
100
|
+
git tag
|
101
|
+
} 2>&1
|
102
|
+
]
|
103
|
+
if $? != 0 then
|
104
|
+
raise RuntimeError, message
|
105
|
+
end
|
106
|
+
|
107
|
+
tags.
|
108
|
+
split(/[\r\n]/).
|
109
|
+
select {|t| t != ''}.
|
110
|
+
map {|t| Gem::Version.create(t)}
|
111
|
+
end
|
112
|
+
|
113
|
+
def update_locked_version()
|
114
|
+
@locked_version =
|
115
|
+
version_contraint.find_the_best_version(list_versions())
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
module Vim
|
2
|
+
module Flavor
|
3
|
+
class FlavorFile
|
4
|
+
attr_reader :flavors
|
5
|
+
|
6
|
+
def initialize()
|
7
|
+
@flavors = {}
|
8
|
+
@default_groups = [:default]
|
9
|
+
end
|
10
|
+
|
11
|
+
def interpret(&block)
|
12
|
+
instance_eval(&block)
|
13
|
+
end
|
14
|
+
|
15
|
+
def eval_flavorfile(flavorfile_path)
|
16
|
+
content = File.open(flavorfile_path, 'rb') do |f|
|
17
|
+
f.read()
|
18
|
+
end
|
19
|
+
interpret do
|
20
|
+
instance_eval(content)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def repo_uri_from_repo_name(repo_name)
|
25
|
+
if /^([^\/]+)$/.match(repo_name) then
|
26
|
+
m = Regexp.last_match
|
27
|
+
"git://github.com/vim-scripts/#{m[1]}.git"
|
28
|
+
elsif /^([A-Za-z0-9_-]+)\/(.*)$/.match(repo_name) then
|
29
|
+
m = Regexp.last_match
|
30
|
+
"git://github.com/#{m[1]}/#{m[2]}.git"
|
31
|
+
elsif /^[a-z]+:\/\/.*$/.match(repo_name) then
|
32
|
+
repo_name
|
33
|
+
else
|
34
|
+
raise "repo_name is written in invalid format: #{repo_name.inspect}"
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def flavor(repo_name, *args)
|
39
|
+
options = Hash === args.last ? args.pop : {}
|
40
|
+
options[:groups] ||= []
|
41
|
+
version_contraint = VersionConstraint.new(args.last || '>= 0')
|
42
|
+
|
43
|
+
f = Flavor.new()
|
44
|
+
f.repo_name = repo_name
|
45
|
+
f.repo_uri = repo_uri_from_repo_name(repo_name)
|
46
|
+
f.version_contraint = version_contraint
|
47
|
+
f.groups = @default_groups + options[:groups]
|
48
|
+
|
49
|
+
@flavors[f.repo_uri] = f
|
50
|
+
end
|
51
|
+
|
52
|
+
def group(*group_names, &block)
|
53
|
+
@default_groups.concat(group_names)
|
54
|
+
yield
|
55
|
+
ensure
|
56
|
+
group_names.each do
|
57
|
+
@default_groups.pop()
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
|
3
|
+
module Vim
|
4
|
+
module Flavor
|
5
|
+
class LockFile
|
6
|
+
# TODO: Resolve dependencies recursively.
|
7
|
+
|
8
|
+
attr_reader :flavors, :path
|
9
|
+
|
10
|
+
def initialize(path)
|
11
|
+
@flavors = {} # repo_uri => flavor
|
12
|
+
@path = path
|
13
|
+
end
|
14
|
+
|
15
|
+
def load()
|
16
|
+
h = File.open(@path, 'rb') do |f|
|
17
|
+
YAML.load(f.read())
|
18
|
+
end
|
19
|
+
|
20
|
+
@flavors = self.class.flavors_from_poro(h[:flavors])
|
21
|
+
end
|
22
|
+
|
23
|
+
def save()
|
24
|
+
h = {}
|
25
|
+
|
26
|
+
h[:flavors] = self.class.poro_from_flavors(@flavors)
|
27
|
+
|
28
|
+
File.open(@path, 'wb') do |f|
|
29
|
+
YAML.dump(h, f)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.poro_from_flavors(flavors)
|
34
|
+
Hash[
|
35
|
+
flavors.values.map {|f|
|
36
|
+
[
|
37
|
+
f.repo_uri,
|
38
|
+
{
|
39
|
+
:groups => f.groups,
|
40
|
+
:locked_version => f.locked_version.to_s(),
|
41
|
+
:repo_name => f.repo_name,
|
42
|
+
:version_contraint => f.version_contraint.to_s(),
|
43
|
+
}
|
44
|
+
]
|
45
|
+
}
|
46
|
+
]
|
47
|
+
end
|
48
|
+
|
49
|
+
def self.flavors_from_poro(poro)
|
50
|
+
Hash[
|
51
|
+
poro.to_a().map {|repo_uri, h|
|
52
|
+
f = Flavor.new()
|
53
|
+
f.groups = h[:groups]
|
54
|
+
f.locked_version = Gem::Version.create(h[:locked_version])
|
55
|
+
f.repo_name = h[:repo_name]
|
56
|
+
f.repo_uri = repo_uri
|
57
|
+
f.version_contraint = VersionConstraint.new(h[:version_contraint])
|
58
|
+
[f.repo_uri, f]
|
59
|
+
}
|
60
|
+
]
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
data/lib/vim-flavor/version.rb
CHANGED
@@ -0,0 +1,48 @@
|
|
1
|
+
module Vim
|
2
|
+
module Flavor
|
3
|
+
class VersionConstraint
|
4
|
+
attr_reader :base_version, :operator
|
5
|
+
|
6
|
+
def initialize(s)
|
7
|
+
@base_version, @operator = parse(s)
|
8
|
+
end
|
9
|
+
|
10
|
+
def to_s()
|
11
|
+
"#{@operator} #{@base_version}"
|
12
|
+
end
|
13
|
+
|
14
|
+
def ==(other)
|
15
|
+
self.base_version == other.base_version &&
|
16
|
+
self.operator == other.operator
|
17
|
+
end
|
18
|
+
|
19
|
+
def parse(s)
|
20
|
+
m = /^\s*(>=|~>)\s+(\S+)$/.match(s)
|
21
|
+
if m then
|
22
|
+
[Gem::Version.create(m[2]), m[1]]
|
23
|
+
else
|
24
|
+
raise "Invalid version constraint: #{s.inspect}"
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def compatible?(other_version_or_s)
|
29
|
+
v = Gem::Version.create(other_version_or_s)
|
30
|
+
if @operator == '~>' then
|
31
|
+
self.base_version.bump() > v and v >= self.base_version
|
32
|
+
elsif @operator == '>=' then
|
33
|
+
v >= self.base_version
|
34
|
+
else
|
35
|
+
raise NotImplementedError
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def find_the_best_version(versions)
|
40
|
+
versions.
|
41
|
+
select {|v| compatible?(v)}.
|
42
|
+
sort().
|
43
|
+
reverse().
|
44
|
+
first
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
data/spec/facade_spec.rb
CHANGED
@@ -250,7 +250,7 @@ describe Vim::Flavor::Facade do
|
|
250
250
|
@flavor = Vim::Flavor::Flavor.new()
|
251
251
|
@flavor.repo_name = '@test_repo_path'
|
252
252
|
@flavor.repo_uri = @test_repo_path
|
253
|
-
@flavor.locked_version = '1.0.0'
|
253
|
+
@flavor.locked_version = Gem::Version.create('1.0.0')
|
254
254
|
|
255
255
|
@flavors = [@flavor]
|
256
256
|
|
data/spec/flavor_spec.rb
CHANGED
@@ -28,7 +28,7 @@ describe Vim::Flavor::Flavor do
|
|
28
28
|
@flavor = described_class.new()
|
29
29
|
@flavor.repo_name = '@test_repo_path'
|
30
30
|
@flavor.repo_uri = @test_repo_path
|
31
|
-
@flavor.locked_version = '1.0.0'
|
31
|
+
@flavor.locked_version = Gem::Version.create('1.0.0')
|
32
32
|
end
|
33
33
|
|
34
34
|
it 'should clone the repository into a given path' do
|
@@ -51,7 +51,7 @@ describe Vim::Flavor::Flavor do
|
|
51
51
|
@flavor = described_class.new()
|
52
52
|
@flavor.repo_name = '@test_repo_path'
|
53
53
|
@flavor.repo_uri = @test_repo_path
|
54
|
-
@flavor.locked_version = '1.0.0'
|
54
|
+
@flavor.locked_version = Gem::Version.create('1.0.0')
|
55
55
|
end
|
56
56
|
|
57
57
|
it 'should fail if the repository is not cloned yet' do
|
@@ -91,7 +91,7 @@ describe Vim::Flavor::Flavor do
|
|
91
91
|
@flavor = described_class.new()
|
92
92
|
@flavor.repo_name = '@test_repo_path'
|
93
93
|
@flavor.repo_uri = @test_repo_path
|
94
|
-
@flavor.locked_version = '1.0.0'
|
94
|
+
@flavor.locked_version = Gem::Version.create('1.0.0')
|
95
95
|
|
96
96
|
@vimfiles_path = "#{@tmp_path}/vimfiles"
|
97
97
|
@deploy_path = @flavor.make_deploy_path(@vimfiles_path)
|
@@ -117,7 +117,7 @@ describe Vim::Flavor::Flavor do
|
|
117
117
|
$?.should == 0
|
118
118
|
tag_id = %x{
|
119
119
|
cd #{@flavor.cached_repo_path.inspect} &&
|
120
|
-
git rev-list -n1 #{@flavor.locked_version
|
120
|
+
git rev-list -n1 '#{@flavor.locked_version}'
|
121
121
|
}
|
122
122
|
$?.should == 0
|
123
123
|
head_id.should == tag_id
|
@@ -181,7 +181,7 @@ describe Vim::Flavor::Flavor do
|
|
181
181
|
@flavor = described_class.new()
|
182
182
|
@flavor.repo_name = '@test_repo_path'
|
183
183
|
@flavor.repo_uri = @test_repo_path
|
184
|
-
@flavor.locked_version = '1.0.0'
|
184
|
+
@flavor.locked_version = Gem::Version.create('1.0.0')
|
185
185
|
|
186
186
|
@vimfiles_path = "#{@tmp_path}/vimfiles"
|
187
187
|
@deploy_path = @flavor.make_deploy_path(@vimfiles_path)
|
@@ -209,7 +209,7 @@ describe Vim::Flavor::Flavor do
|
|
209
209
|
@flavor = described_class.new()
|
210
210
|
@flavor.repo_name = '@test_repo_path'
|
211
211
|
@flavor.repo_uri = @test_repo_path
|
212
|
-
@flavor.locked_version = '1.0.0'
|
212
|
+
@flavor.locked_version = Gem::Version.create('1.0.0')
|
213
213
|
end
|
214
214
|
|
215
215
|
it 'should list tags as versions' do
|
metadata
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
name: vim-flavor
|
3
3
|
version: !ruby/object:Gem::Version
|
4
4
|
prerelease:
|
5
|
-
version: 0.0.
|
5
|
+
version: 0.0.3
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- Kana Natsuno
|
@@ -10,7 +10,7 @@ autorequire:
|
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
12
|
|
13
|
-
date: 2012-
|
13
|
+
date: 2012-04-06 00:00:00 +09:00
|
14
14
|
default_executable:
|
15
15
|
dependencies:
|
16
16
|
- !ruby/object:Gem::Dependency
|
@@ -53,7 +53,14 @@ files:
|
|
53
53
|
- Rakefile
|
54
54
|
- bin/vim-flavor
|
55
55
|
- lib/vim-flavor.rb
|
56
|
+
- lib/vim-flavor/cli.rb
|
57
|
+
- lib/vim-flavor/facade.rb
|
58
|
+
- lib/vim-flavor/flavor.rb
|
59
|
+
- lib/vim-flavor/flavorfile.rb
|
60
|
+
- lib/vim-flavor/lockfile.rb
|
61
|
+
- lib/vim-flavor/stringextension.rb
|
56
62
|
- lib/vim-flavor/version.rb
|
63
|
+
- lib/vim-flavor/versionconstraint.rb
|
57
64
|
- spec/cli_spec.rb
|
58
65
|
- spec/facade_spec.rb
|
59
66
|
- spec/flavor_spec.rb
|