linecook 1.2.1 → 2.0.0

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