vim-flavor 0.0.4 → 1.0.0

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.
Files changed (52) hide show
  1. data/.rspec +1 -0
  2. data/.travis.yml +4 -0
  3. data/README.md +3 -174
  4. data/Rakefile +10 -0
  5. data/features/.nav +12 -0
  6. data/features/README.md +17 -0
  7. data/features/caching.feature +50 -0
  8. data/features/command_line/README.md +9 -0
  9. data/features/command_line/progress_messages.feature +44 -0
  10. data/features/flavorfile/README.md +9 -0
  11. data/features/flavorfile/comments.feature +24 -0
  12. data/features/flavorfile/repository_name.feature +53 -0
  13. data/features/flavorfile/version_constraint.feature +52 -0
  14. data/features/install_vim_flavor.md +42 -0
  15. data/features/philosophy.md +90 -0
  16. data/features/resolve_dependencies.feature +16 -0
  17. data/features/step_definitions/bootstrap_script_steps.rb +7 -0
  18. data/features/step_definitions/cli_steps.rb +34 -0
  19. data/features/step_definitions/directory_steps.rb +23 -0
  20. data/features/step_definitions/flavor_steps.rb +37 -0
  21. data/features/step_definitions/flavorfile_steps.rb +12 -0
  22. data/features/step_definitions/lockfile_steps.rb +13 -0
  23. data/features/support/env.rb +49 -0
  24. data/features/typical_usage/README.md +63 -0
  25. data/features/typical_usage/deploy_to_arbitrary_place.feature +24 -0
  26. data/features/typical_usage/install_vim_plugins.feature +26 -0
  27. data/features/typical_usage/uninstall_vim_plugins.feature +31 -0
  28. data/features/typical_usage/upgrade_vim_plugins.feature +27 -0
  29. data/features/uninstall_vim_flavor.md +16 -0
  30. data/features/version_lock.feature +26 -0
  31. data/lib/vim-flavor.rb +2 -12
  32. data/lib/vim-flavor/cli.rb +16 -12
  33. data/lib/vim-flavor/enumerableextension.rb +48 -0
  34. data/lib/vim-flavor/facade.rb +65 -102
  35. data/lib/vim-flavor/flavor.rb +70 -63
  36. data/lib/vim-flavor/flavorfile.rb +15 -47
  37. data/lib/vim-flavor/lockfile.rb +27 -44
  38. data/lib/vim-flavor/lockfileparser.rb +65 -0
  39. data/lib/vim-flavor/stringextension.rb +25 -1
  40. data/lib/vim-flavor/version.rb +1 -1
  41. data/lib/vim-flavor/versionconstraint.rb +12 -11
  42. data/spec/enumerableextension_spec.rb +100 -0
  43. data/spec/facade_spec.rb +49 -540
  44. data/spec/flavor_spec.rb +50 -250
  45. data/spec/flavorfile_spec.rb +34 -110
  46. data/spec/lockfile_spec.rb +79 -89
  47. data/spec/spec_helper.rb +0 -65
  48. data/spec/stringextension_spec.rb +10 -6
  49. data/spec/versionconstraint_spec.rb +37 -119
  50. data/vim-flavor.gemspec +3 -1
  51. metadata +135 -46
  52. data/spec/cli_spec.rb +0 -15
@@ -0,0 +1,31 @@
1
+ @typical_usage
2
+ Feature: Uninstall Vim plugins
3
+ In order to automate boring steps,
4
+ as a lazy Vim user,
5
+ I want to use a declarative way to delete Vim plugins from my configuration.
6
+
7
+ Background:
8
+ Given a temporary directory called 'tmp'
9
+ And a home directory called 'home' in '$tmp/home'
10
+ And a repository 'foo' with versions '1.0.0 1.0.1 1.0.2'
11
+ And a repository 'bar' with versions '2.0.0 2.0.1 2.0.2'
12
+
13
+ Scenario: Install after deleting some flavors in flavorfile
14
+ Given flavorfile
15
+ """ruby
16
+ flavor '$foo_uri'
17
+ flavor '$bar_uri'
18
+ """
19
+ And I run `vim-flavor install`
20
+ When I edit flavorfile as
21
+ """ruby
22
+ flavor '$bar_uri'
23
+ """
24
+ And I run `vim-flavor install` again
25
+ Then I get lockfile
26
+ """
27
+ $bar_uri (2.0.2)
28
+ """
29
+ And I get a bootstrap script in '$home/.vim'
30
+ And I get flavor '$bar_uri' with '2.0.2' in '$home/.vim'
31
+ But I don't have flavor '$foo_uri' in '$home/.vim'
@@ -0,0 +1,27 @@
1
+ @typical_usage
2
+ Feature: Upgrade Vim plugins
3
+ In order to automate boring steps,
4
+ as a lazy Vim user,
5
+ I want to use a declarative way to upgrade my favorite Vim plugins.
6
+
7
+ Background:
8
+ Given a temporary directory called 'tmp'
9
+ And a home directory called 'home' in '$tmp/home'
10
+ And a repository 'foo' with versions '1.0.0 1.0.1 1.0.2'
11
+
12
+ Scenario: Upgrade with lockfile
13
+ Given flavorfile
14
+ """ruby
15
+ flavor '$foo_uri'
16
+ """
17
+ And lockfile
18
+ """
19
+ $foo_uri (1.0.0)
20
+ """
21
+ When I run `vim-flavor upgrade`
22
+ Then I get lockfile
23
+ """
24
+ $foo_uri (1.0.2)
25
+ """
26
+ And I get a bootstrap script in '$home/.vim'
27
+ And I get flavor '$foo_uri' with '1.0.2' in '$home/.vim'
@@ -0,0 +1,16 @@
1
+ ## Uninstallation steps
2
+
3
+ rm -r ~/.vim-flavor
4
+ rm -r ~/.vim/flavors # or ~/vimfiles/flavors etc.
5
+
6
+ cd $YOUR_REPOSITORY_FOR_DOTFILES
7
+ rm VimFlavor VimFlavor.lock
8
+ vim vimrc # Remove the "runtime flavors/bootstrap.vim" line.
9
+ git commit -am 'Farewell to vim-flavor'
10
+
11
+ gem uninstall vim-flavor
12
+
13
+
14
+
15
+
16
+ <!-- vim: set expandtab shiftwidth=4 softtabstop=4 textwidth=78 : -->
@@ -0,0 +1,26 @@
1
+ Feature: Version lock
2
+ In order to use the same configuration on every machine,
3
+ as a lazy Vim user,
4
+ I want to keep versions of Vim plugins which I installed before.
5
+
6
+ Background:
7
+ Given a temporary directory called 'tmp'
8
+ And a home directory called 'home' in '$tmp/home'
9
+ And a repository 'foo' with versions '1.0.0 1.0.1 1.0.2'
10
+
11
+ Scenario: Install with lockfile
12
+ Given flavorfile
13
+ """ruby
14
+ flavor '$foo_uri'
15
+ """
16
+ And lockfile
17
+ """
18
+ $foo_uri (1.0.0)
19
+ """
20
+ When I run `vim-flavor install`
21
+ Then I get lockfile
22
+ """
23
+ $foo_uri (1.0.0)
24
+ """
25
+ And I get a bootstrap script in '$home/.vim'
26
+ And I get flavor '$foo_uri' with '1.0.0' in '$home/.vim'
data/lib/vim-flavor.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  require 'bundler/setup'
2
+ require 'vim-flavor/enumerableextension'
2
3
 
3
4
  module Vim
4
5
  module Flavor
@@ -8,6 +9,7 @@ module Vim
8
9
  :Flavor,
9
10
  :FlavorFile,
10
11
  :LockFile,
12
+ :LockFileParser,
11
13
  :StringExtension,
12
14
  :VERSION,
13
15
  :VersionConstraint,
@@ -18,17 +20,5 @@ module Vim
18
20
  class ::String
19
21
  include StringExtension
20
22
  end
21
-
22
- class << self
23
- @@dot_path = File.expand_path('~/.vim-flavor')
24
-
25
- def dot_path
26
- @@dot_path
27
- end
28
-
29
- def dot_path= path
30
- @@dot_path = path
31
- end
32
- end
33
23
  end
34
24
  end
@@ -5,25 +5,29 @@ module Vim
5
5
  class CLI < Thor
6
6
  desc 'install', 'Install Vim plugins according to VimFlavor file.'
7
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()
8
+ :desc => 'Where to install Vim plugins.',
9
+ :banner => 'DIR'
10
+ def install
11
+ Facade.new().install(
12
+ options[:vimfiles_path] || default_vimfiles_path
14
13
  )
15
14
  end
16
15
 
17
16
  desc 'upgrade', 'Upgrade Vim plugins according to VimFlavor file.'
18
17
  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()
18
+ :desc => 'Where to install Vim plugins.',
19
+ :banner => 'DIR'
20
+ def upgrade
21
+ Facade.new().upgrade(
22
+ options[:vimfiles_path] || default_vimfiles_path
25
23
  )
26
24
  end
25
+
26
+ no_tasks do
27
+ def default_vimfiles_path
28
+ ENV['HOME'].to_vimfiles_path
29
+ end
30
+ end
27
31
  end
28
32
  end
29
33
  end
@@ -0,0 +1,48 @@
1
+ module Vim
2
+ module Flavor
3
+ EnumerableExtension = ::Enumerable
4
+
5
+ module EnumerableExtension
6
+ def before_each(&block)
7
+ vs = each
8
+ Enumerator.new do |y|
9
+ loop do
10
+ v = vs.next
11
+ block.call(v)
12
+ y << v
13
+ end
14
+ end
15
+ end
16
+
17
+ def after_each(&block)
18
+ vs = each
19
+ Enumerator.new do |y|
20
+ loop do
21
+ v = vs.next
22
+ y << v
23
+ block.call(v)
24
+ end
25
+ end
26
+ end
27
+
28
+ def on_failure(&block)
29
+ vs = each
30
+ Enumerator.new do |y|
31
+ v = nil
32
+ begin
33
+ loop do
34
+ v = vs.next
35
+ y << v
36
+ v = nil
37
+ end
38
+ rescue StopIteration
39
+ raise
40
+ rescue
41
+ block.call(v)
42
+ raise
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
@@ -3,56 +3,85 @@ require 'fileutils'
3
3
  module Vim
4
4
  module Flavor
5
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
6
+ def trace message
7
+ print message
18
8
  end
19
9
 
20
- def trace(message)
21
- print(message) if @traced
10
+ def refresh_flavors(mode, vimfiles_path)
11
+ flavorfile = FlavorFile.load(Dir.getwd().to_flavorfile_path)
12
+ lockfile = LockFile.load_or_new(Dir.getwd().to_lockfile_path)
13
+
14
+ lockfile.update(
15
+ complete(
16
+ flavorfile.flavor_table,
17
+ lockfile.flavor_table,
18
+ mode
19
+ )
20
+ )
21
+ lockfile.save()
22
+
23
+ deploy_flavors(lockfile.flavors, vimfiles_path)
24
+
25
+ trace "Completed.\n"
22
26
  end
23
27
 
24
- def load()
25
- @flavorfile = FlavorFile.new()
26
- @flavorfile.eval_flavorfile(@flavorfile_path)
28
+ def install(vimfiles_path)
29
+ refresh_flavors(:install, vimfiles_path)
30
+ end
27
31
 
28
- @lockfile = LockFile.new(@lockfile_path)
29
- @lockfile.load() if File.exists?(@lockfile_path)
32
+ def upgrade(vimfiles_path)
33
+ refresh_flavors(:upgrade, vimfiles_path)
30
34
  end
31
35
 
32
- def make_new_flavors(current_flavors, locked_flavors, mode)
33
- new_flavors = {}
36
+ def complete(current_flavor_table, locked_flavor_table, mode)
37
+ completed_flavor_table = {}
38
+
39
+ trace "Checking versions...\n"
40
+
41
+ current_flavor_table.values.map(&:dup).sort_by(&:repo_name).
42
+ before_each {|nf| trace " Use #{nf.repo_name} ..."}.
43
+ after_each {|nf| trace " #{nf.locked_version}\n"}.
44
+ on_failure {trace " failed\n"}.
45
+ each do |nf|
46
+ lf = locked_flavor_table[nf.repo_name]
34
47
 
35
- current_flavors.each do |repo_uri, cf|
36
- lf = locked_flavors[repo_uri]
37
- nf = cf.dup()
48
+ already_cached = nf.cached?
49
+ nf.clone() unless already_cached
38
50
 
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
51
+ if mode == :install and lf and nf.satisfied_with?(lf)
52
+ nf.use_specific_version(lf.locked_version)
53
+ else
54
+ nf.fetch() if already_cached
55
+ nf.use_appropriate_version()
56
+ end
47
57
 
48
- new_flavors[repo_uri] = nf
58
+ completed_flavor_table[nf.repo_name] = nf
49
59
  end
50
60
 
51
- new_flavors
61
+ completed_flavor_table
62
+ end
63
+
64
+ def deploy_flavors(flavors, vimfiles_path)
65
+ trace "Deploying plugins...\n"
66
+
67
+ FileUtils.rm_rf(
68
+ ["#{vimfiles_path.to_flavors_path}"],
69
+ :secure => true
70
+ )
71
+
72
+ create_vim_script_for_bootstrap(vimfiles_path)
73
+
74
+ flavors.
75
+ before_each {|f| trace " #{f.repo_name} #{f.locked_version} ..."}.
76
+ after_each {|f| trace " done\n"}.
77
+ on_failure {trace " failed\n"}.
78
+ each do |f|
79
+ f.deploy(vimfiles_path)
80
+ end
52
81
  end
53
82
 
54
83
  def create_vim_script_for_bootstrap(vimfiles_path)
55
- bootstrap_path = "#{vimfiles_path.to_flavors_path()}/bootstrap.vim"
84
+ bootstrap_path = vimfiles_path.to_flavors_path.to_bootstrap_path
56
85
  FileUtils.mkdir_p(File.dirname(bootstrap_path))
57
86
  File.open(bootstrap_path, 'w') do |f|
58
87
  f.write(<<-'END')
@@ -84,73 +113,7 @@ module Vim
84
113
  END
85
114
  end
86
115
  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
116
  end
155
117
  end
156
118
  end
119
+
@@ -1,118 +1,125 @@
1
1
  module Vim
2
2
  module Flavor
3
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
4
+ # A short name of a repository.
5
+ # Possible formats are "$user/$repo", "$repo" and "$repo_uri".
6
+ attr_accessor :repo_name
15
7
 
16
- def initialize()
17
- @groups = []
18
- end
8
+ # A constraint to choose a proper version.
9
+ attr_accessor :version_constraint
19
10
 
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
11
+ # A version of a plugin to be installed.
12
+ attr_accessor :locked_version
26
13
 
27
- def zapped_repo_dir_name
28
- @repo_name.gsub(/[^A-Za-z0-9._-]/, '_')
14
+ # Return true if this flavor's repository is already cloned.
15
+ def cached?
16
+ Dir.exists?(cached_repo_path)
29
17
  end
30
18
 
31
19
  def cached_repo_path
32
20
  @cached_repo_path ||=
33
- "#{Vim::Flavor.dot_path}/repos/#{zapped_repo_dir_name}"
21
+ "#{ENV['HOME'].to_stash_path}/repos/#{@repo_name.zap}"
34
22
  end
35
23
 
36
- def make_deploy_path(vimfiles_path)
37
- "#{vimfiles_path.to_flavors_path()}/#{zapped_repo_dir_name}"
24
+ def self.github_repo_uri(user, repo)
25
+ @github_repo_uri ||= lambda {|user, repo|
26
+ "git://github.com/#{user}/#{repo}.git"
27
+ }
28
+ @github_repo_uri.call(user, repo)
29
+ end
30
+
31
+ def repo_uri
32
+ @repo_uri ||=
33
+ if /^([^\/]+)$/.match(repo_name)
34
+ m = Regexp.last_match
35
+ self.class.github_repo_uri('vim-scripts', m[1])
36
+ elsif /^([A-Za-z0-9_-]+)\/(.*)$/.match(repo_name)
37
+ m = Regexp.last_match
38
+ self.class.github_repo_uri(m[1], m[2])
39
+ elsif /^[a-z]+:\/\/.*$/.match(repo_name)
40
+ repo_name
41
+ else
42
+ raise "Invalid repo_name: #{repo_name.inspect}"
43
+ end
38
44
  end
39
45
 
40
46
  def clone()
41
- message = %x[
47
+ sh %Q[
42
48
  {
43
- git clone '#{@repo_uri}' '#{cached_repo_path}'
49
+ git clone '#{repo_uri}' '#{cached_repo_path}'
44
50
  } 2>&1
45
51
  ]
46
- if $? != 0 then
47
- raise RuntimeError, message
48
- end
49
52
  true
50
53
  end
51
54
 
52
55
  def fetch()
53
- message = %x[
56
+ sh %Q{
54
57
  {
55
- cd #{cached_repo_path.inspect} &&
56
- git fetch origin
58
+ cd '#{cached_repo_path}' &&
59
+ git fetch --tags
57
60
  } 2>&1
58
- ]
59
- if $? != 0 then
60
- raise RuntimeError, message
61
- end
61
+ }
62
62
  end
63
63
 
64
64
  def deploy(vimfiles_path)
65
- deploy_path = make_deploy_path(vimfiles_path)
66
- message = %x[
65
+ deployment_path = "#{vimfiles_path.to_flavors_path}/#{repo_name.zap}"
66
+ sh %Q[
67
67
  {
68
68
  cd '#{cached_repo_path}' &&
69
69
  git checkout -f '#{locked_version}' &&
70
- git checkout-index -a -f --prefix='#{deploy_path}/' &&
70
+ git checkout-index -a -f --prefix='#{deployment_path}/' &&
71
71
  {
72
72
  vim -u NONE -i NONE -n -N -e -s -c '
73
- silent! helptags #{deploy_path}/doc
73
+ silent! helptags #{deployment_path}/doc
74
74
  qall!
75
75
  ' || true
76
76
  }
77
77
  } 2>&1
78
78
  ]
79
- if $? != 0 then
80
- raise RuntimeError, message
81
- end
79
+ true
82
80
  end
83
81
 
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
82
+ def use_appropriate_version()
83
+ @locked_version =
84
+ version_constraint.find_the_best_version(list_versions)
94
85
  end
95
86
 
96
- def list_versions()
97
- tags = %x[
87
+ def use_specific_version(locked_version)
88
+ @locked_version = locked_version
89
+ end
90
+
91
+ def list_tags()
92
+ output = sh %Q[
98
93
  {
99
94
  cd '#{cached_repo_path}' &&
100
95
  git tag
101
96
  } 2>&1
102
97
  ]
103
- if $? != 0 then
104
- raise RuntimeError, message
105
- end
98
+ output.split(/[\r\n]/)
99
+ end
106
100
 
101
+ def versions_from_tags(tags)
107
102
  tags.
108
- split(/[\r\n]/).
109
103
  select {|t| t != '' && Gem::Version.correct?(t)}.
110
104
  map {|t| Gem::Version.create(t)}
111
105
  end
112
106
 
113
- def update_locked_version()
114
- @locked_version =
115
- version_contraint.find_the_best_version(list_versions())
107
+ def list_versions()
108
+ versions_from_tags(list_tags())
109
+ end
110
+
111
+ def sh script
112
+ output = send(:`, script)
113
+ if $? == 0
114
+ output
115
+ else
116
+ raise RuntimeError, output
117
+ end
118
+ end
119
+
120
+ def satisfied_with?(locked_flavor)
121
+ repo_name == locked_flavor.repo_name &&
122
+ version_constraint.compatible?(locked_flavor.locked_version)
116
123
  end
117
124
  end
118
125
  end