vim-flavor 0.0.4 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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