homedir 3.0.alpha2 → 3.0.alpha4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. data/.travis.yml +0 -1
  2. data/Guardfile +8 -4
  3. data/README.mkd +11 -1
  4. data/homedir.gemspec +1 -0
  5. data/lib/homedir.rb +3 -2
  6. data/lib/homedir/catalog.rb +4 -1
  7. data/lib/homedir/cli.rb +34 -3
  8. data/lib/homedir/errors.rb +4 -0
  9. data/lib/homedir/hacks.rb +9 -0
  10. data/lib/homedir/package.rb +47 -18
  11. data/lib/homedir/package_discovery_loader.rb +29 -0
  12. data/lib/homedir/package_loader.rb +48 -0
  13. data/lib/homedir/package_version1_loader.rb +70 -0
  14. data/lib/homedir/package_version2_loader.rb +48 -0
  15. data/lib/homedir/package_version3_loader.rb +45 -0
  16. data/lib/homedir/version.rb +1 -1
  17. data/spec/acceptance/load_packages_spec.rb +17 -0
  18. data/spec/examples/version1-example/.homedir.control +11 -0
  19. data/spec/examples/version1-example/combined/version1-file +3 -0
  20. data/spec/examples/version1-example/version1/separate-file +2 -0
  21. data/spec/examples/version2-example/.homedir/control +13 -0
  22. data/spec/examples/version2-example/.homedir/post-install +2 -0
  23. data/spec/examples/version2-example/.homedir/post-remove +2 -0
  24. data/spec/examples/version2-example/.homedir/pre-install +2 -0
  25. data/spec/examples/version2-example/.homedir/pre-remove +2 -0
  26. data/spec/examples/version2-example/combined/version2-file +3 -0
  27. data/spec/examples/version2-example/version2/separate-file +2 -0
  28. data/spec/examples/version3-example/.gitignore +1 -0
  29. data/spec/examples/version3-example/description.txt +3 -0
  30. data/spec/examples/version3-example/homedir.yml +4 -0
  31. data/spec/examples/version3-example/homedir/combined/version3-file +3 -0
  32. data/spec/examples/version3-example/homedir/version3/separate-file +2 -0
  33. data/spec/examples/version3-example/post-install.sh +2 -0
  34. data/spec/examples/version3-example/post-remove.sh +2 -0
  35. data/spec/examples/version3-example/post-update.sh +2 -0
  36. data/spec/examples/version3-example/pre-install.sh +2 -0
  37. data/spec/examples/version3-example/pre-remove.sh +2 -0
  38. data/spec/examples/version3-example/pre-update.sh +2 -0
  39. data/spec/integration/catalog_packages_spec.rb +27 -0
  40. data/spec/lib/homedir/package_loader_spec.rb +30 -0
  41. data/spec/lib/homedir/package_spec.rb +49 -4
  42. data/spec/lib/homedir/package_version1_loader_spec.rb +66 -0
  43. data/spec/lib/homedir/package_version2_loader_spec.rb +41 -0
  44. data/spec/lib/homedir/package_version3_loader_spec.rb +41 -0
  45. data/spec/spec_helper.rb +15 -8
  46. data/spec/support/package_shared_examples.rb +59 -0
  47. metadata +93 -20
data/.travis.yml CHANGED
@@ -1,5 +1,4 @@
1
1
  rvm:
2
- - jruby
3
2
  - 1.8.7
4
3
  - 1.9.2
5
4
  - 1.9.3
data/Guardfile CHANGED
@@ -7,11 +7,15 @@ guard 'bundler' do
7
7
  # watch(/^.+\.gemspec/)
8
8
  end
9
9
 
10
- guard 'rspec', :version => 2, :cli => "--color --format doc --fail-fast" do
10
+ guard 'rspec', :version => 2, :cli => "--color --format doc" do
11
11
  watch(%r{^spec/.+_spec\.rb$})
12
- watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
13
- watch('spec/spec_helper.rb') { "spec" }
14
- watch('spec/factories.rb') { "spec" }
12
+ watch(%r{^lib/}) { "spec/integration" }
13
+ #watch(%r{^(lib|bin)/}) { "spec/acceptance" }
14
+ watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
15
+ watch('spec/spec_helper.rb') { "spec" }
16
+ watch('spec/factories.rb') { "spec" }
17
+ watch(%r{^spec/support/.+\.rb}) { "spec" }
18
+ watch(%r{^spec/examples/}) { "spec" }
15
19
  end
16
20
 
17
21
 
data/README.mkd CHANGED
@@ -22,7 +22,17 @@ That's it!
22
22
 
23
23
  ## Support
24
24
 
25
- You can find me on freenode's IRC servers as the user 'docwhat'. I'm usually on the #homedir channel.
25
+ * File an [issue](https://github.com/docwhat/homedir/issues).
26
+ * Freenode IRC in [`#homedir`](http://webchat.freenode.net/?channels=rvm).
27
+ * Contact me [directly](http://docwhat.org/email).
28
+
29
+ ## Contributing
30
+
31
+ I love having people help me. All changes are welcome!
32
+
33
+ * Submit a [pull request](https://github.com/docwhat/homedir/pulls)
34
+ * Add to the [wiki](https://github.com/docwhat/homedir/wiki).
35
+ * Use the github instant editing feature.
26
36
 
27
37
  ## The Story So Far…
28
38
 
data/homedir.gemspec CHANGED
@@ -24,6 +24,7 @@ Gem::Specification.new do |gem|
24
24
  gem.add_development_dependency('factory_girl', ["~> 2.5.2"])
25
25
 
26
26
  gem.add_development_dependency('yard')
27
+ gem.add_development_dependency('redcarpet')
27
28
 
28
29
  gem.add_development_dependency('guard')
29
30
  gem.add_development_dependency('guard-rspec')
data/lib/homedir.rb CHANGED
@@ -2,8 +2,9 @@
2
2
  # COPYRIGHT:: Copyright (c) 2012 Christian Höltje
3
3
  # LICENSE:: The MIT License. See the file LICENSE distributed with this source.
4
4
  require 'homedir/package'
5
+ require 'homedir/catalog'
6
+ require 'homedir/package_discovery_loader'
7
+ require 'homedir/errors'
5
8
 
6
9
  module Homedir
7
- class Error < StandardError
8
- end
9
10
  end
@@ -1,8 +1,11 @@
1
1
  require 'homedir/errors'
2
+ require 'set'
2
3
 
3
4
  module Homedir
4
- class Catalog < Set
5
+ class Catalog < ::Set
5
6
 
7
+ # Find the package with the name `name`
8
+ #
6
9
  # @param {String} name The name of the {Homedir::Package package} to find.
7
10
  # @return {Homedir::Package} The package that matches the `name`
8
11
  def find_by_name name
data/lib/homedir/cli.rb CHANGED
@@ -1,4 +1,6 @@
1
1
  require 'thor'
2
+ require 'homedir'
3
+ require 'pathname'
2
4
 
3
5
  module Homedir
4
6
  # The command line interface for {Homedir}.
@@ -7,7 +9,28 @@ module Homedir
7
9
  # information
8
10
  class CLI < Thor
9
11
 
10
- desc "list", "List all known packages."
12
+ no_tasks do
13
+ # The directories to scan for repositories
14
+ #
15
+ # @return {Enumerable} A list of Pathname objects
16
+ def repositories
17
+ [ Pathname.new(ENV['HOME']) + '.homedir' + 'packages']
18
+ end
19
+
20
+ # Helper method to initialize the catalog, if needed.
21
+ #
22
+ # @return {Homedir::Catalog} A populated catalog
23
+ def catalog
24
+ @catalog ||= Catalog.new.tap do |cat|
25
+ pdl = PackageDiscoveryLoader.new(cat)
26
+ repositories.each do |repo|
27
+ pdl.load_from_directory(repo)
28
+ end
29
+ end
30
+ end
31
+ end
32
+
33
+ desc "list", "Lists available packages."
11
34
  method_option(
12
35
  :remote,
13
36
  :type => :boolean,
@@ -15,8 +38,11 @@ module Homedir
15
38
  :aliases => '-r',
16
39
  :description => "Queries the remote server")
17
40
  def list
18
- # FIXME: list needs to do something
19
- puts "Not implemented yet"
41
+ packages = catalog.to_a # TODO sort
42
+ puts "#{packages.length} local packages found:"
43
+ packages.each do |package|
44
+ puts " * #{package}"
45
+ end
20
46
  end
21
47
 
22
48
  desc "info PACKAGE", "Describe a specific PACKAGE in detail."
@@ -50,6 +76,11 @@ module Homedir
50
76
  puts "Not implemented yet #{package_name.inspect}"
51
77
  end
52
78
 
79
+ desc "repair", "Scans through your home directory and repairs all links. Warning: This can take a long time."
80
+ def repair
81
+ # FIXME: needs to do something
82
+ puts "Not implemented yet"
83
+ end
53
84
  end
54
85
  end
55
86
 
@@ -14,4 +14,8 @@ module Homedir
14
14
  # message for the list of acceptable characters in a name.
15
15
  class InvalidNameError < Error
16
16
  end
17
+
18
+ # The directory does not contain a package
19
+ class InvalidPackageDirectoryError < Error
20
+ end
17
21
  end
@@ -0,0 +1,9 @@
1
+ require 'fileutils'
2
+ require 'pathname'
3
+
4
+ # This is adds a couple of methods to make my Homedir work better.
5
+ class Pathname
6
+ def mkdir_p *args
7
+ FileUtils.mkdir_p(self.to_s, *args)
8
+ end
9
+ end
@@ -20,12 +20,16 @@ module Homedir
20
20
  :directory,
21
21
  ].freeze
22
22
  DEFAULT_VALUES = {
23
- :name => nil,
24
- :description => nil,
25
- :dependencies => [],
26
- :post_install => nil,
27
- :pre_uninstall => nil,
28
- :directory => nil,
23
+ :name => nil,
24
+ :description => nil,
25
+ :dependencies => [],
26
+ :pre_install => nil,
27
+ :post_install => nil,
28
+ :pre_remove => nil,
29
+ :post_remove => nil,
30
+ :pre_update => nil,
31
+ :post_update => nil,
32
+ :directory => nil,
29
33
  }.freeze
30
34
 
31
35
  # The name of the package.
@@ -44,16 +48,40 @@ module Homedir
44
48
  # A list of package names this package depends on.
45
49
  #
46
50
  # When assigning a list, they can either be {Homedir::Package Package} objects or strings.
47
- # @return [Enumerable] A list of package {#name names}.
51
+ # @return [Set] The set of package {#name names}.
48
52
  attr_reader :dependencies
49
53
 
50
- # A script that should run after install.
54
+ # A script to run before a package is enabled.
55
+ #
56
+ # @return {String}
57
+ attr_accessor :pre_install
58
+
59
+ # A script to run after a package is enabled.
51
60
  #
52
61
  # @return {String}
53
62
  attr_accessor :post_install
54
63
 
55
- # A script to run before install.
56
- attr_accessor :pre_uninstall
64
+ # A script to run before the package is disabled.
65
+ # @return {String}
66
+ attr_accessor :pre_remove
67
+
68
+ # A script to run after the package is disabled
69
+ # @return {String}
70
+ attr_accessor :post_remove
71
+
72
+ # A script to run before upgrading the package's source
73
+ #
74
+ # This is run before any upgrade steps run for the package source.
75
+ # @return {String}
76
+ attr_accessor :pre_update
77
+
78
+ # A script that runs after upgrading the package's source
79
+ #
80
+ # This is run after a package is upgraded and after the
81
+ # the package has been re-enabled but before the pre_install
82
+ # script.
83
+ # @return {String}
84
+ attr_accessor :post_update
57
85
 
58
86
  # The directory where the package is stored.
59
87
  #
@@ -62,15 +90,15 @@ module Homedir
62
90
  # @return {Pathname}
63
91
  attr_reader :directory
64
92
 
65
- # Create a new {Homedir::Package Package} instance.
93
+ # Create a new {Package} instance.
66
94
  #
67
95
  # @param {Hash} options
68
- # @option options [String] :name The name of the package.
69
- # @option options [String] :description The description of the package.
70
- # @option options [Enumerable] :dependencies A list of {Homedir::Package packages} or {String strings}.
71
- # @option options [Pathname] :directory The directory the packages is located at.
72
- # @option options [String] :post_install Commands to run after installing the package.
73
- # @option options [String] :pre_uninstall Commands to run before uninstalling the package.
96
+ # @option options {String} :name The name of the package.
97
+ # @option options {String} :description The description of the package.
98
+ # @option options {Enumerable} :dependencies A list of {Homedir::Package packages} or {String strings}.
99
+ # @option options {Pathname} :directory The directory the packages is located at.
100
+ # @option options {String} :post_install Commands to run after installing the package.
101
+ # @option options {String} :pre_uninstall Commands to run before uninstalling the package.
74
102
  def initialize(options = {})
75
103
  options = DEFAULT_VALUES.merge(options)
76
104
 
@@ -100,7 +128,7 @@ module Homedir
100
128
 
101
129
  # {include:#dependencies}
102
130
  def dependencies= value
103
- @dependencies = value.map { |p| p.to_s }
131
+ @dependencies = Set.new( value.map { |p| p.to_s } )
104
132
  end
105
133
 
106
134
  # {include:#directory}
@@ -159,5 +187,6 @@ module Homedir
159
187
  def to_s
160
188
  @name
161
189
  end
190
+
162
191
  end
163
192
  end
@@ -0,0 +1,29 @@
1
+ require 'homedir/package_loader'
2
+
3
+ module Homedir
4
+ class PackageDiscoveryLoader
5
+ attr_reader :catalog
6
+ def initialize(catalog=nil)
7
+ @catalog = catalog
8
+ end
9
+
10
+ def loader
11
+ @loader ||= PackageLoader.new
12
+ end
13
+
14
+ # Recursively loads packages from the directory into {#catalog}
15
+ #
16
+ # @params {Pathname} path The directory to start scanning from
17
+ # @return {Homedir::PackageDiscoveryLoader} self
18
+ def load_from_directory path
19
+ if loader.path_is_valid?(path)
20
+ catalog << loader.load_from_path(path)
21
+ else
22
+ path.children.each do |child|
23
+ load_from_directory(child) if child.directory?
24
+ end
25
+ end
26
+ return self
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,48 @@
1
+ require 'homedir/errors'
2
+ require 'homedir/package_version1_loader'
3
+ require 'homedir/package_version2_loader'
4
+ require 'homedir/package_version3_loader'
5
+
6
+ module Homedir
7
+ class PackageLoader
8
+
9
+ # The list of loaders, in the order they should be tried
10
+ #
11
+ # @return {Enumerable} A list of package loaders
12
+ def loaders
13
+ # The order is important
14
+ @loaders ||= [
15
+ PackageVersion3Loader.new,
16
+ PackageVersion2Loader.new,
17
+ PackageVersion1Loader.new,
18
+ ].freeze
19
+ end
20
+
21
+ # Loads a package from a path
22
+ #
23
+ # @param {Pathname} path The directory of the package
24
+ # @return {Homedir::Package} The package object
25
+ def load_from_path path
26
+ loaders.each do |loader|
27
+ begin
28
+ return loader.load_from_path(path) if loader.path_is_valid?(path)
29
+ rescue InvalidPackageDirectoryError
30
+ next
31
+ end
32
+ end
33
+
34
+ raise InvalidPackageDirectoryError.new("The directory '#{path}' doesn't contain a valid package")
35
+ end
36
+
37
+ # Is the path a valid version1 package directory?
38
+ #
39
+ # @return {Boolean} Returns true if the path is a valid package directory.
40
+ def path_is_valid? path
41
+ loaders.each do |loader|
42
+ return true if loader.path_is_valid?(path)
43
+ end
44
+ return false
45
+ end
46
+
47
+ end
48
+ end
@@ -0,0 +1,70 @@
1
+ require 'homedir/package'
2
+ require 'homedir/errors'
3
+
4
+ module Homedir
5
+ class PackageVersion1Loader
6
+
7
+ # Loads a package from a path
8
+ #
9
+ # @param {Pathname} path The directory of the package
10
+ # @return {Homedir::Package} The package object
11
+ def load_from_path(path)
12
+ raise InvalidPackageDirectoryError.new("The directory '#{path}' is not a version 1 package.") unless path_is_valid?(path)
13
+ control_path = path + '.homedir.control'
14
+
15
+ control = parse_control_file control_path
16
+
17
+ pkg = Package.new( :directory => path )
18
+
19
+ pkg.name = control[:package]
20
+ pkg.description = control[:description] || ''
21
+ pkg.dependencies = (control[:depends] || '').split("\n")
22
+
23
+ return pkg
24
+ end
25
+
26
+ # Is the path a valid version1 package directory?
27
+ #
28
+ # @return {Boolean} Returns true if the path is a valid package directory.
29
+ def path_is_valid? path
30
+ return false unless (path).directory?
31
+ return false unless (path + '.homedir.control').file?
32
+ return true
33
+ end
34
+
35
+ # Parse a control file
36
+ #
37
+ # @param {Pathname} path The path to control file
38
+ # @return {Hash} A hash containing the keys/values.
39
+ # @see {#parse_control_stream}
40
+ def parse_control_file path
41
+ path.open('r') { |f| parse_control_stream f }
42
+ end
43
+
44
+ # Parse a version 1 control from a stream
45
+ #
46
+ # This is *not* a robust parser.
47
+ # @param {IO} stream An IO stream to read from
48
+ # @return {Hash} A hash containing the keys/values.
49
+ def parse_control_stream stream
50
+ contents = stream.read()
51
+
52
+ hash = {}
53
+ last_key = nil
54
+ last_value = nil
55
+ contents.split(/[\r\n]+/).each do |line|
56
+ next if line =~ /^\s*#/
57
+ if line =~ /^\s/
58
+ last_value << line.strip unless last_key.nil?
59
+ elsif line.include? ":"
60
+ hash[last_key.to_sym] = last_value.join("\n") unless last_key.nil?
61
+ last_key, value = line.split(/\s*:\s*/, 2)
62
+ last_value = value.strip.empty? ? [] : [ value.strip ]
63
+ end
64
+ end
65
+ hash[last_key.to_sym] = last_value.join("\n") unless last_key.nil?
66
+
67
+ return hash
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,48 @@
1
+ require 'homedir/package'
2
+ require 'homedir/package_version1_loader'
3
+ require 'homedir/errors'
4
+
5
+ module Homedir
6
+ class PackageVersion2Loader < PackageVersion1Loader
7
+
8
+ # Loads a package from a path
9
+ #
10
+ # @param {Pathname} path The directory of the package
11
+ # @return {Homedir::Package} The package object
12
+ def load_from_path(path)
13
+ raise InvalidPackageDirectoryError.new("The directory '#{path}' is not a version 2 package.") unless path_is_valid?(path)
14
+ control_path = path + '.homedir' + 'control'
15
+
16
+ control = parse_control_file control_path
17
+
18
+ pkg = Package.new( :directory => path )
19
+
20
+ pkg.name = control[:package]
21
+ pkg.description = control[:description] || ''
22
+ pkg.dependencies = (control[:depends] || '').split("\n")
23
+
24
+ ['pre', 'post'].each do |prefix|
25
+ ['install', 'remove'].each do |action|
26
+ filepath = (path + ".homedir" + "#{prefix}-#{action}")
27
+ if filepath.file? and filepath.executable?
28
+ filepath.open('r') do |f|
29
+ pkg.send("#{prefix}_#{action}=", f.read())
30
+ end
31
+ end
32
+ end
33
+ end
34
+
35
+ return pkg
36
+ end
37
+
38
+ # Is the path a valid version2 package directory?
39
+ #
40
+ # @return {Boolean} Returns true if the path is a valid package directory.
41
+ def path_is_valid? path
42
+ return false unless (path).directory?
43
+ return false unless (path + '.homedir').directory?
44
+ return false unless (path + '.homedir' + 'control').file?
45
+ return true
46
+ end
47
+ end
48
+ end