linecook 1.2.1 → 2.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 (61) hide show
  1. data/{History → History.rdoc} +3 -2
  2. data/README.rdoc +93 -0
  3. data/bin/linecook +32 -56
  4. data/bin/linecook_run +19 -6
  5. data/bin/linecook_scp +12 -4
  6. data/doc/vm_setup.rdoc +75 -0
  7. data/lib/linecook.rb +3 -2
  8. data/lib/linecook/attributes.rb +33 -8
  9. data/lib/linecook/command.rb +61 -0
  10. data/lib/linecook/command_set.rb +85 -0
  11. data/lib/linecook/command_utils.rb +20 -0
  12. data/lib/linecook/commands/build.rb +108 -57
  13. data/lib/linecook/commands/compile.rb +181 -0
  14. data/lib/linecook/commands/{helper.rb → compile_helper.rb} +123 -94
  15. data/lib/linecook/commands/run.rb +43 -39
  16. data/lib/linecook/commands/snapshot.rb +24 -24
  17. data/lib/linecook/commands/ssh.rb +7 -7
  18. data/lib/linecook/commands/start.rb +10 -10
  19. data/lib/linecook/commands/state.rb +7 -7
  20. data/lib/linecook/commands/stop.rb +3 -3
  21. data/lib/linecook/commands/{vbox_command.rb → virtual_box_command.rb} +31 -29
  22. data/lib/linecook/cookbook.rb +149 -131
  23. data/lib/linecook/executable.rb +28 -0
  24. data/lib/linecook/package.rb +177 -361
  25. data/lib/linecook/proxy.rb +4 -10
  26. data/lib/linecook/recipe.rb +289 -369
  27. data/lib/linecook/test.rb +114 -98
  28. data/lib/linecook/utils.rb +31 -41
  29. data/lib/linecook/version.rb +2 -6
  30. metadata +120 -68
  31. data/HowTo/Control Virtual Machines +0 -106
  32. data/HowTo/Generate Scripts +0 -268
  33. data/HowTo/Run Scripts +0 -87
  34. data/HowTo/Setup Virtual Machines +0 -76
  35. data/README +0 -117
  36. data/lib/linecook/commands.rb +0 -11
  37. data/lib/linecook/commands/command.rb +0 -58
  38. data/lib/linecook/commands/command_error.rb +0 -12
  39. data/lib/linecook/commands/env.rb +0 -89
  40. data/lib/linecook/commands/init.rb +0 -86
  41. data/lib/linecook/commands/package.rb +0 -57
  42. data/lib/linecook/template.rb +0 -17
  43. data/lib/linecook/test/command_parser.rb +0 -75
  44. data/lib/linecook/test/file_test.rb +0 -197
  45. data/lib/linecook/test/regexp_escape.rb +0 -86
  46. data/lib/linecook/test/shell_test.rb +0 -177
  47. data/lib/linecook/test/shim.rb +0 -71
  48. data/templates/Gemfile +0 -3
  49. data/templates/Rakefile +0 -146
  50. data/templates/_gitignore +0 -4
  51. data/templates/attributes/project_name.rb +0 -3
  52. data/templates/config/ssh +0 -14
  53. data/templates/cookbook +0 -10
  54. data/templates/files/example.txt +0 -1
  55. data/templates/helpers/project_name/echo.erb +0 -4
  56. data/templates/packages/abox.yml +0 -2
  57. data/templates/project_name.gemspec +0 -30
  58. data/templates/recipes/abox.rb +0 -16
  59. data/templates/templates/example.erb +0 -1
  60. data/templates/test/project_name_test.rb +0 -24
  61. data/templates/test/test_helper.rb +0 -14
@@ -1,8 +1,8 @@
1
- require 'linecook/commands/vbox_command'
1
+ require 'linecook/commands/virtual_box_command'
2
2
 
3
3
  module Linecook
4
4
  module Commands
5
-
5
+
6
6
  # :startdoc::desc take a vm snapshop
7
7
  #
8
8
  # Takes the specified snapshot of one or more VirtualBox virtual machines.
@@ -20,14 +20,14 @@ module Linecook
20
20
  #
21
21
  # To reset:
22
22
  #
23
- # liencook snapshot --reset CURRENT
23
+ # linecook snapshot --reset CURRENT
24
24
  #
25
25
  # After which there will only be a single 'CURRENT' snapshot, which
26
26
  # corresponds to the original snapshot.
27
27
  #
28
- class Snapshot < VboxCommand
29
- config :reset, false, :long => :reset, &c.flag # reset a snapshot
30
-
28
+ class Snapshot < VirtualBoxCommand
29
+ config :reset, false # -r, --reset : reset a snapshot
30
+
31
31
  def process(snapshot, *hosts)
32
32
  vm_names = resolve_vm_names(hosts)
33
33
  each_vm_name(vm_names) do |vm_name|
@@ -38,75 +38,75 @@ module Linecook
38
38
  end
39
39
  end
40
40
  end
41
-
41
+
42
42
  def parse_snapshots(vm_name)
43
43
  info = `VBoxManage -q showvminfo #{vm_name}`
44
44
  snapshots = {}
45
-
45
+
46
46
  stack = [{}]
47
47
  parent = nil
48
-
48
+
49
49
  info.each_line do |line|
50
50
  next unless line =~ /^(\s+)Name\: (.*?) \(/
51
51
  depth = $1.length / 3
52
52
  name = $2
53
-
53
+
54
54
  if depth > stack.length
55
55
  stack.push stack.last[parent]
56
56
  elsif depth < stack.length
57
57
  stack.pop
58
58
  end
59
-
59
+
60
60
  snapshot = {}
61
61
  snapshots[name] = snapshot
62
62
  stack.last[name] = snapshot
63
63
  parent = name
64
64
  end
65
-
65
+
66
66
  snapshots
67
67
  end
68
-
68
+
69
69
  def reset_snapshot(vm_name, snapshot)
70
70
  stop(vm_name) if running?(vm_name)
71
-
71
+
72
72
  snapshot = snapshot.upcase
73
73
  restore(vm_name, snapshot)
74
-
74
+
75
75
  snapshots = parse_snapshots(vm_name)
76
76
  parent = snapshots.keys.select {|key| key =~ /\A#{snapshot}(?:_\d+)\z/ }.first
77
77
  parent ||= snapshot
78
-
78
+
79
79
  children = snapshots[parent]
80
80
  children.each do |key, value|
81
81
  inside_out_each(key, value) do |child|
82
82
  sh! "VBoxManage -q snapshot #{vm_name} delete #{child}"
83
83
  end
84
84
  end
85
-
85
+
86
86
  unless parent == snapshot
87
87
  sh! "VBoxManage -q snapshot #{vm_name} edit #{parent} --name #{snapshot}"
88
88
  end
89
89
  end
90
-
90
+
91
91
  def snapshot(vm_name, snapshot)
92
92
  snapshot = snapshot.upcase
93
93
  snapshots = parse_snapshots(vm_name)
94
-
94
+
95
95
  count = snapshots.keys.grep(/\A#{snapshot}(?:_|\z)/).length
96
96
  if count > 0
97
97
  sh! "VBoxManage -q snapshot #{vm_name} edit #{snapshot} --name #{snapshot}_#{count - 1}"
98
98
  end
99
-
100
- sh! "VBoxManage -q snapshot #{vm_name} take #{snapshot}"
99
+
100
+ sh! "VBoxManage -q snapshot #{vm_name} take #{snapshot} --pause"
101
101
  end
102
-
102
+
103
103
  private
104
-
104
+
105
105
  def inside_out_each(key, value, &block) # :nodoc:
106
106
  value.each_pair do |k, v|
107
107
  inside_out_each(k, v, &block)
108
108
  end
109
-
109
+
110
110
  yield(key)
111
111
  end
112
112
  end
@@ -1,24 +1,24 @@
1
- require 'linecook/commands/vbox_command'
1
+ require 'linecook/commands/virtual_box_command'
2
2
 
3
3
  module Linecook
4
4
  module Commands
5
-
5
+
6
6
  # :startdoc::desc ssh to a vm
7
7
  #
8
8
  # Connects to a virtual machine using ssh, as configured in config/ssh.
9
- #
10
- class Ssh < VboxCommand
9
+ #
10
+ class Ssh < VirtualBoxCommand
11
11
  undef_config :names
12
-
12
+
13
13
  def default_host
14
14
  load_hosts(ssh_config_file).collect {|host, vm_name| host }.first
15
15
  end
16
-
16
+
17
17
  def process(host=default_host)
18
18
  if host.to_s.strip.empty?
19
19
  raise CommandError.new("no host specified")
20
20
  end
21
-
21
+
22
22
  ssh = "ssh -F '#{ssh_config_file}' '#{host}' --"
23
23
 
24
24
  # Patterned after vagrant/ssh.rb (circa 0.6.6)
@@ -1,18 +1,18 @@
1
- require 'linecook/commands/vbox_command'
1
+ require 'linecook/commands/virtual_box_command'
2
2
 
3
3
  module Linecook
4
4
  module Commands
5
-
5
+
6
6
  # :startdoc::desc start a vm
7
7
  #
8
8
  # Starts one or more VirtualBox virtual machines, and resets to a snapshot
9
9
  # if provided. By default all virtual machines configured in config/ssh
10
10
  # will be reset and started in this way.
11
- class Start < VboxCommand
12
- config :type, 'headless' # vm type (headless|gui)
13
- config :snapshot, '', :short => :s # start snapshot
14
- config :socket, false, &c.flag
15
-
11
+ class Start < VirtualBoxCommand
12
+ config :type, 'headless' # vm type (headless|gui)
13
+ config :snapshot, '' # start snapshot
14
+ config :use_master_socket, false # -m, --master-socket : use a master socket
15
+
16
16
  def process(*hosts)
17
17
  vm_names = resolve_vm_names(hosts)
18
18
  each_vm_name(vm_names) do |vm_name|
@@ -20,13 +20,13 @@ module Linecook
20
20
  stop(vm_name)
21
21
  sleep 0.5
22
22
  end
23
-
23
+
24
24
  unless snapshot.empty?
25
25
  restore(vm_name, snapshot)
26
26
  end
27
-
27
+
28
28
  start(vm_name, type)
29
- start_ssh_socket(vm_name) if socket
29
+ start_ssh_socket(vm_name) if use_master_socket
30
30
  end
31
31
  end
32
32
  end
@@ -1,20 +1,20 @@
1
- require 'linecook/commands/vbox_command'
1
+ require 'linecook/commands/virtual_box_command'
2
2
 
3
3
  module Linecook
4
4
  module Commands
5
-
6
- # :startdoc::desc print the vm state
5
+
6
+ # :startdoc::desc print a vm state
7
7
  #
8
8
  # Prints the state of one or more VirtualBox virtual machines. By default
9
9
  # all virtual machines configured in config/ssh will print their state.
10
10
  #
11
- class State < VboxCommand
12
- config :hosts, false, :short => :n, &c.flag # print state by host
13
-
11
+ class State < VirtualBoxCommand
12
+ config :hosts, false # -s, --hosts : print state by host
13
+
14
14
  def state(vm_name)
15
15
  `VBoxManage showvminfo #{vm_name}` =~ /^State:\s+(.*)$/ ? $1 : 'unknown'
16
16
  end
17
-
17
+
18
18
  def process(*hosts)
19
19
  vm_names = resolve_vm_names(hosts)
20
20
  if hosts
@@ -1,14 +1,14 @@
1
- require 'linecook/commands/vbox_command'
1
+ require 'linecook/commands/virtual_box_command'
2
2
 
3
3
  module Linecook
4
4
  module Commands
5
-
5
+
6
6
  # :startdoc::desc stop a vm
7
7
  #
8
8
  # Stops one or more VirtualBox virtual machines using 'poweroff'. By
9
9
  # default all virtual machines configured in config/ssh will be stopped.
10
10
  #
11
- class Stop < VboxCommand
11
+ class Stop < VirtualBoxCommand
12
12
  def process(*hosts)
13
13
  vm_names = resolve_vm_names(hosts)
14
14
  each_vm_name(vm_names) do |vm_name|
@@ -1,14 +1,16 @@
1
- require 'linecook/commands/command'
1
+ require 'linecook/command'
2
+ require 'linecook/command_utils'
2
3
 
3
4
  module Linecook
4
5
  module Commands
5
- class VboxCommand < Command
6
- registry.delete_if {|key, value| value == self }
7
-
8
- config :ssh_config_file, 'config/ssh', :writer => false # the ssh config file
9
- config :quiet, false, &c.flag # silence output
10
- config :names, false, &c.flag # use vm names
11
-
6
+ class VirtualBoxCommand < Command
7
+ include CommandUtils
8
+
9
+ config :ssh_config_file, 'config/ssh', { # -F FILE : the ssh config file
10
+ :writer => :ssh_config_file=
11
+ }
12
+ config :names, false # -n, --names : use vm names
13
+
12
14
  # Matches a host declaration in a ssh config file. After the match:
13
15
  #
14
16
  # $1:: The host name
@@ -17,7 +19,7 @@ module Linecook
17
19
  # (ex: 'Host name # [vm_name]' => 'vm_name')
18
20
  #
19
21
  HOST_REGEXP = /^\s*Host\s+([^\s#]+)(?:\s*#\s*\[(.*?)\])?/
20
-
22
+
21
23
  # Returns a hash of (host, vm_name) pairs as declared in a ssh config
22
24
  # file. Basically this means parsing out the name in each config like:
23
25
  #
@@ -30,7 +32,7 @@ module Linecook
30
32
  #
31
33
  def load_hosts(ssh_config_file)
32
34
  hosts = []
33
-
35
+
34
36
  File.open(ssh_config_file) do |io|
35
37
  io.each_line do |line|
36
38
  next unless line =~ HOST_REGEXP
@@ -38,90 +40,90 @@ module Linecook
38
40
  hosts << [$1, $2 || $1]
39
41
  end
40
42
  end
41
-
43
+
42
44
  hosts
43
45
  end
44
-
46
+
45
47
  def ssh_config_file=(ssh_config_file)
46
48
  @ssh_config_file = ssh_config_file
47
49
  @host_list = nil
48
50
  @host_map = nil
49
51
  end
50
-
52
+
51
53
  def host_list
52
54
  @host_list ||= load_hosts(ssh_config_file)
53
55
  end
54
-
56
+
55
57
  def host_map
56
58
  @host_map ||= Hash[*host_list.flatten]
57
59
  end
58
-
60
+
59
61
  def resolve_vm_names(hosts)
60
62
  names ? hosts : hosts.collect {|host| host_map[host] || host }
61
63
  end
62
-
64
+
63
65
  def each_host(hosts=[])
64
66
  if hosts.empty?
65
67
  hosts = host_list.collect {|host, vm_name| host }
66
68
  hosts.delete('*')
67
69
  end
68
-
70
+
69
71
  hosts.uniq.each do |host|
70
72
  yield(host)
71
73
  end
72
74
  end
73
-
75
+
74
76
  def each_vm_name(vm_names=[])
75
77
  if vm_names.empty?
76
78
  vm_names = host_list.collect {|host, vm_name| vm_name }
77
79
  vm_names.delete('*')
78
80
  end
79
-
81
+
80
82
  vm_names.uniq.each do |vm_name|
81
83
  yield(vm_name)
82
84
  end
83
85
  end
84
-
86
+
85
87
  def ssh(host, cmd)
86
88
  sh "ssh -q -F '#{ssh_config_file}' '#{host}' -- #{cmd}"
87
89
  end
88
-
90
+
89
91
  def ssh!(host, cmd)
90
92
  unless ssh(host, cmd)
91
93
  raise CommandError, "non-zero exit status: #{$?.exitstatus}"
92
94
  end
93
95
  end
94
-
96
+
95
97
  def running?(vm_name)
96
98
  `VBoxManage -q list runningvms`.include?(vm_name)
97
99
  end
98
-
100
+
99
101
  def start(vm_name, type='headless')
100
102
  sh! "VBoxManage -q startvm #{vm_name} --type #{type}"
101
103
  end
102
-
104
+
103
105
  def stop(vm_name)
104
106
  sh! "VBoxManage -q controlvm #{vm_name} poweroff"
105
107
  end
106
-
108
+
107
109
  def restore(vm_name, snapshot)
108
110
  sh! "VBoxManage -q snapshot #{vm_name} restore #{snapshot.upcase}"
109
111
  end
110
-
112
+
111
113
  def discardstate(vm_name)
112
114
  sh! "VBoxManage discardstate #{vm_name}"
113
115
  end
114
-
116
+
115
117
  def share(vm_name, name, local_dir, remote_dir)
116
118
  share_dir = "#{local_dir}/#{vm_name}"
117
119
  FileUtils.mkdir_p(share_dir) unless File.exists?(share_dir)
118
-
120
+
119
121
  ssh vm_name, "sudo umount '#{remote_dir}' > /dev/null 2>&1"
120
122
  sh "VBoxManage sharedfolder remove '#{vm_name}' --name '#{name}' --transient > /dev/null 2>&1"
121
123
  sh! "VBoxManage sharedfolder add '#{vm_name}' --name '#{name}' --hostpath '#{share_dir}' --transient"
122
124
  ssh! vm_name, "sudo mount -t vboxsf -o uid=1000,gid=100 '#{name}' '#{remote_dir}'"
123
125
  end
124
-
126
+
125
127
  def start_ssh_socket(vm_name)
126
128
  sh "ssh -MNf -F '#{ssh_config_file}' '#{vm_name}' >/dev/null 2>&1 </dev/null"
127
129
  end
@@ -1,160 +1,178 @@
1
- require 'linecook/utils'
2
- require 'lazydoc'
3
-
4
1
  module Linecook
5
2
  class Cookbook
6
3
  class << self
7
- def config_file(project_dir='.')
8
- Dir.glob(File.join(project_dir, '{C,c}ookbook')).first
9
- end
10
-
11
- def setup(config={}, project_dir='.')
12
- unless config.kind_of?(Hash)
13
- config = Utils.load_config(config)
14
- end
15
-
16
- config[PATHS_KEY] ||= [project_dir]
17
- config[GEMS_KEY] ||= gems
18
-
19
- new(config, project_dir)
4
+ attr_writer :default_path_map
5
+ def default_path_map
6
+ @default_path_map ||= {
7
+ :attributes => ['attributes'],
8
+ :files => ['files'],
9
+ :templates => ['templates'],
10
+ :recipes => ['recipes']
11
+ }
20
12
  end
21
-
22
- def init(project_dir='.')
23
- setup config_file(project_dir), project_dir
13
+
14
+ attr_writer :default_file_name
15
+ def default_file_name
16
+ @default_file_name ||= 'cookbook.yml'
24
17
  end
25
-
26
- def gems
18
+
19
+ def gemspecs(latest=true)
27
20
  return [] unless Object.const_defined?(:Gem)
28
-
29
- Gem.source_index.latest_specs.select do |spec|
30
- config_file(spec.full_gem_path) != nil
31
- end.collect do |spec|
32
- spec.name
21
+
22
+ index = Gem.source_index
23
+ specs = latest ? index.latest_specs : index.gems.values
24
+
25
+ specs.select do |spec|
26
+ cookbook_file = File.expand_path(default_file_name, spec.full_gem_path)
27
+ File.exists?(cookbook_file)
33
28
  end
34
29
  end
35
30
  end
36
-
37
- MANIFEST_KEY = 'manifest'
38
- PATHS_KEY = 'paths'
39
- GEMS_KEY = 'gems'
40
- REWRITE_KEY = 'rewrite'
41
-
42
- PATTERNS = [
43
- ['attributes', '**/*{.rb,.yaml,.yml,.json}', true],
44
- ['files', '**/*'],
45
- ['recipes', '**/*.rb', true],
46
- ['templates', '**/*']
47
- ]
48
-
49
- attr_reader :project_dir
50
- attr_reader :config
51
-
52
- def initialize(config={}, project_dir='.')
53
- @project_dir = project_dir
54
- @config = config
55
- end
56
-
57
- def paths
58
- Utils.arrayify config[PATHS_KEY]
59
- end
60
-
61
- def gems
62
- Utils.arrayify config[GEMS_KEY]
63
- end
64
-
65
- def rewrites
66
- config[REWRITE_KEY]
31
+
32
+ attr_reader :registry
33
+
34
+ def initialize(*project_dirs)
35
+ @registry = {}
36
+ project_dirs.each do |dir|
37
+ case dir
38
+ when String then add(dir)
39
+ when Hash then add('.', dir)
40
+ else add(*dir)
41
+ end
42
+ end
67
43
  end
68
-
69
- def overrides
70
- config[MANIFEST_KEY]
44
+
45
+ # Returns an array of directories comprising the path for type.
46
+ def path(type)
47
+ registry[type] || []
71
48
  end
72
-
73
- def full_gem_paths
74
- return [] if gems.empty?
75
- specs = latest_specs
76
-
77
- gems.collect do |name|
78
- spec = specs[name] or raise "no such gem: #{name.inspect}"
79
- spec.full_gem_path
49
+
50
+ def add(dir, path_map=nil)
51
+ resolve_path_map(dir, path_map).each_pair do |type, paths|
52
+ (registry[type] ||= []).concat(paths)
80
53
  end
81
54
  end
82
-
83
- def rewrite(manifest)
84
- replacements = {}
85
-
86
- rewrites.each_pair do |pattern, substitution|
87
- manifest.keys.each do |key|
88
- replacement = key.sub(pattern, substitution)
89
- next if key == replacement
90
- raise "multiple replacements for: #{key}" if replacements.has_key?(key)
91
-
92
- replacements[key] = replacement
55
+
56
+ def rm(dir, path_map=nil)
57
+ resolve_path_map(dir, path_map).each_pair do |type, paths|
58
+ if current = registry[type]
59
+ current = current - paths
60
+ if current.empty?
61
+ registry.delete(type)
62
+ else
63
+ registry[type] = current
64
+ end
93
65
  end
94
- end if rewrites
95
-
96
- replacements.each do |key, replacement|
97
- manifest[replacement] = manifest.delete(key)
98
66
  end
99
-
100
- manifest
101
67
  end
102
-
103
- def glob(*paths)
104
- manifest = Hash.new {|hash, key| hash[key] = {} }
105
-
106
- paths.each do |path|
107
- PATTERNS.each do |(type, glob, chomp_extname)|
108
- resource_dir = File.expand_path(File.join(path, type), project_dir)
109
- pattern = File.join(resource_dir, glob)
110
-
111
- Dir.glob(pattern).each do |full_path|
112
- next unless File.file?(full_path)
113
-
114
- name = relative_path(resource_dir, full_path)
115
- name.chomp!(File.extname(full_path)) if chomp_extname
116
-
117
- manifest[type][name] = full_path
68
+
69
+ # Same as find but returns nil if no file can be found.
70
+ def _find_(type, filename, extnames=nil)
71
+ if type.nil? || filename.nil?
72
+ return nil
73
+ end
74
+
75
+ if absolute?(filename)
76
+ return File.exists?(filename) ? filename : nil
77
+ end
78
+
79
+ path(type).each do |dir|
80
+ each_full_path(dir, filename, extnames) do |full_path|
81
+ if File.exists?(full_path) && subpath?(dir, full_path)
82
+ return full_path
118
83
  end
119
84
  end
120
85
  end
121
-
122
- manifest
86
+
87
+ nil
88
+ end
89
+
90
+ # Searches for a file by expanding filename vs each directory in the path
91
+ # for type. The first existing full path is returned. If an array of
92
+ # extnames is provided, then each extname is tried for each directory,
93
+ # much as with Kernal.require. Absolute paths that exists are returned
94
+ # directly. Raises an error if the file cannot be found.
95
+ def find(type, source_name, extnames=nil)
96
+ _find_(type, source_name, extnames) or begin
97
+ case
98
+ when type.nil?
99
+ raise "could not find file: #{source_name.inspect} (nil type specified)"
100
+ when source_name.nil?
101
+ raise "could not find file: #{source_name.inspect}"
102
+ when absolute?(source_name)
103
+ raise "no such file: #{source_name.inspect}"
104
+ else
105
+ try_string = try_extnames?(source_name, extnames) ? " (tried #{extnames.join(', ')})" : nil
106
+ raise "could not find file: #{source_name.inspect}#{try_string}"
107
+ end
108
+ end
123
109
  end
124
-
125
- def manifest
126
- manifest = glob(*(full_gem_paths + paths))
127
-
128
- if overrides
129
- manifest = Utils.deep_merge(manifest, overrides)
110
+
111
+ protected
112
+
113
+ if Dir.pwd[0] == ?/
114
+ def absolute?(path)
115
+ path && path[0] == ?/
130
116
  end
131
-
132
- manifest.each_key do |key|
133
- manifest[key] = rewrite manifest[key]
117
+ else
118
+ def absolute?(path)
119
+ path && path =~ /^[A-z]:\//
134
120
  end
135
-
136
- manifest
137
121
  end
138
-
139
- def merge(config={})
140
- duplicate = dup
141
- dup.config.merge!(config)
142
- dup
122
+
123
+ def subpath?(dir, full_path)
124
+ full_path.index(dir) == 0
125
+ end
126
+
127
+ def try_extnames?(path, extnames)
128
+ extnames && File.extname(path).empty?
129
+ end
130
+
131
+ def each_full_path(dir, path, extnames=nil)
132
+ full_path = File.expand_path(path, dir)
133
+ yield full_path
134
+
135
+ if try_extnames?(path, extnames)
136
+ extnames.each do |extname|
137
+ full_path = File.expand_path("#{path}#{extname}", dir)
138
+ yield full_path
139
+ end
140
+ end
143
141
  end
144
-
145
- private
146
-
147
- def relative_path(dir, path) # :nodoc:
148
- start = dir.length + 1
149
- path[start, path.length - start]
142
+
143
+ def resolve_path_map(dir, path_map=nil)
144
+ path_map ||= self.class.default_file_name
145
+
146
+ case path_map
147
+ when Hash
148
+ expand_path_map(dir, path_map)
149
+ when String
150
+ cookbook_file = File.expand_path(path_map, dir)
151
+ path_map = File.exists?(cookbook_file) ? YAML.load_file(cookbook_file) : nil
152
+ path_map ||= self.class.default_path_map
153
+
154
+ unless path_map.kind_of?(Hash)
155
+ raise "could not load path map: #{cookbook_file.inspect} (does not load a Hash)"
156
+ end
157
+
158
+ expand_path_map(dir, path_map)
159
+ else
160
+ raise "could not resolve path map: #{path_map.inspect} (must be String, Hash, or nil)"
161
+ end
150
162
  end
151
-
152
- def latest_specs # :nodoc:
153
- latest = {}
154
- Gem.source_index.latest_specs.each do |spec|
155
- latest[spec.name] = spec
163
+
164
+ def expand_path_map(dir, path_map)
165
+ results = Hash.new {|hash, key| hash[key] = [] }
166
+
167
+ path_map.each_pair do |type, paths|
168
+ unless paths.kind_of?(Array)
169
+ paths = [paths]
170
+ end
171
+ paths.each do |path|
172
+ results[type.to_sym] << File.expand_path(path, dir)
173
+ end
156
174
  end
157
- latest
175
+ results
158
176
  end
159
177
  end
160
178
  end