rubycut-babushka 0.10.6

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 (171) hide show
  1. data/Gemfile +8 -0
  2. data/Gemfile.lock +31 -0
  3. data/README.markdown +246 -0
  4. data/Rakefile +26 -0
  5. data/bin/babushka +11 -0
  6. data/deps/babushka.rb +101 -0
  7. data/deps/dev.rb +12 -0
  8. data/deps/fhs.rb +31 -0
  9. data/deps/git.rb +29 -0
  10. data/deps/homebrew.rb +30 -0
  11. data/deps/os_x.rb +33 -0
  12. data/deps/packages.rb +22 -0
  13. data/deps/pkg_managers.rb +110 -0
  14. data/deps/ruby.rb +23 -0
  15. data/deps/rubygems.rb +24 -0
  16. data/deps/system.rb +10 -0
  17. data/deps/templates/app.rb +68 -0
  18. data/deps/templates/external.rb +12 -0
  19. data/deps/templates/installer.rb +31 -0
  20. data/deps/templates/managed.rb +105 -0
  21. data/deps/templates/ppa.rb +24 -0
  22. data/deps/templates/src.rb +42 -0
  23. data/deps/templates/tmbundle.rb +15 -0
  24. data/lib/babushka.rb +28 -0
  25. data/lib/babushka/accepts_block_for.rb +72 -0
  26. data/lib/babushka/accepts_list_for.rb +49 -0
  27. data/lib/babushka/accepts_value_for.rb +24 -0
  28. data/lib/babushka/base.rb +78 -0
  29. data/lib/babushka/bug_reporter.rb +55 -0
  30. data/lib/babushka/cmdline.rb +133 -0
  31. data/lib/babushka/cmdline/handler.rb +41 -0
  32. data/lib/babushka/cmdline/helpers.rb +127 -0
  33. data/lib/babushka/cmdline/parser.rb +69 -0
  34. data/lib/babushka/colorizer.rb +59 -0
  35. data/lib/babushka/core_patches/array.rb +171 -0
  36. data/lib/babushka/core_patches/blank.rb +22 -0
  37. data/lib/babushka/core_patches/bytes.rb +52 -0
  38. data/lib/babushka/core_patches/hash.rb +107 -0
  39. data/lib/babushka/core_patches/hashish.rb +14 -0
  40. data/lib/babushka/core_patches/integer.rb +25 -0
  41. data/lib/babushka/core_patches/io.rb +8 -0
  42. data/lib/babushka/core_patches/numeric.rb +16 -0
  43. data/lib/babushka/core_patches/object.rb +27 -0
  44. data/lib/babushka/core_patches/string.rb +116 -0
  45. data/lib/babushka/core_patches/symbol.rb +12 -0
  46. data/lib/babushka/core_patches/try.rb +15 -0
  47. data/lib/babushka/core_patches/uri.rb +24 -0
  48. data/lib/babushka/dep.rb +470 -0
  49. data/lib/babushka/dep_context.rb +18 -0
  50. data/lib/babushka/dep_definer.rb +115 -0
  51. data/lib/babushka/dep_pool.rb +49 -0
  52. data/lib/babushka/dep_runner.rb +85 -0
  53. data/lib/babushka/dsl.rb +26 -0
  54. data/lib/babushka/git_repo.rb +185 -0
  55. data/lib/babushka/helpers/git_helpers.rb +32 -0
  56. data/lib/babushka/helpers/log_helpers.rb +176 -0
  57. data/lib/babushka/helpers/path_helpers.rb +34 -0
  58. data/lib/babushka/helpers/run_helpers.rb +145 -0
  59. data/lib/babushka/helpers/shell_helpers.rb +229 -0
  60. data/lib/babushka/helpers/suggest_helpers.rb +16 -0
  61. data/lib/babushka/helpers/uri_helpers.rb +36 -0
  62. data/lib/babushka/ip.rb +160 -0
  63. data/lib/babushka/lambda_chooser.rb +40 -0
  64. data/lib/babushka/levenshtein.rb +125 -0
  65. data/lib/babushka/meta_dep.rb +65 -0
  66. data/lib/babushka/meta_dep_context.rb +15 -0
  67. data/lib/babushka/parameter.rb +143 -0
  68. data/lib/babushka/pkg_helper.rb +81 -0
  69. data/lib/babushka/pkg_helpers/apt_helper.rb +61 -0
  70. data/lib/babushka/pkg_helpers/base_helper.rb +19 -0
  71. data/lib/babushka/pkg_helpers/binpkgsrc_helper.rb +48 -0
  72. data/lib/babushka/pkg_helpers/binports_helper.rb +34 -0
  73. data/lib/babushka/pkg_helpers/brew_helper.rb +110 -0
  74. data/lib/babushka/pkg_helpers/gem_helper.rb +120 -0
  75. data/lib/babushka/pkg_helpers/macports_helper.rb +22 -0
  76. data/lib/babushka/pkg_helpers/npm_helper.rb +45 -0
  77. data/lib/babushka/pkg_helpers/pacman_helper.rb +27 -0
  78. data/lib/babushka/pkg_helpers/pip_helper.rb +45 -0
  79. data/lib/babushka/pkg_helpers/src_helper.rb +16 -0
  80. data/lib/babushka/pkg_helpers/yum_helper.rb +25 -0
  81. data/lib/babushka/popen.rb +40 -0
  82. data/lib/babushka/prompt.rb +176 -0
  83. data/lib/babushka/renderable.rb +67 -0
  84. data/lib/babushka/resource.rb +215 -0
  85. data/lib/babushka/run_reporter.rb +60 -0
  86. data/lib/babushka/shell.rb +108 -0
  87. data/lib/babushka/source.rb +216 -0
  88. data/lib/babushka/source_pool.rb +146 -0
  89. data/lib/babushka/system_definitions.rb +97 -0
  90. data/lib/babushka/system_profile.rb +210 -0
  91. data/lib/babushka/task.rb +142 -0
  92. data/lib/babushka/vars.rb +108 -0
  93. data/lib/babushka/version_of.rb +65 -0
  94. data/lib/babushka/version_str.rb +57 -0
  95. data/lib/babushka/xml_string.rb +28 -0
  96. data/lib/components.rb +82 -0
  97. data/lib/fancypath/fancypath.rb +200 -0
  98. data/lib/inkan/inkan.rb +76 -0
  99. data/spec/acceptance/acceptance.rb +43 -0
  100. data/spec/acceptance_helper.rb +113 -0
  101. data/spec/archives/Blah.app.zip +0 -0
  102. data/spec/archives/archive.tar +0 -0
  103. data/spec/archives/archive.tar.bz2 +0 -0
  104. data/spec/archives/archive.tar.gz +0 -0
  105. data/spec/archives/archive.tbz2 +0 -0
  106. data/spec/archives/archive.tgz +0 -0
  107. data/spec/archives/archive.zip +0 -0
  108. data/spec/archives/content.txt +5 -0
  109. data/spec/archives/invalid_archive +5 -0
  110. data/spec/archives/nested archive/content.txt +5 -0
  111. data/spec/archives/nested_archive.tar +0 -0
  112. data/spec/archives/really_a_gzip.zip +0 -0
  113. data/spec/archives/test-0.3.1.tgz +0 -0
  114. data/spec/archives/tgz_archive +0 -0
  115. data/spec/archives/zip_without_extension +0 -0
  116. data/spec/babushka/accepts_for_spec.rb +174 -0
  117. data/spec/babushka/accepts_for_support.rb +72 -0
  118. data/spec/babushka/cmdline/console_spec.rb +11 -0
  119. data/spec/babushka/cmdline/help_spec.rb +61 -0
  120. data/spec/babushka/cmdline/version_spec.rb +10 -0
  121. data/spec/babushka/core_patches_spec.rb +171 -0
  122. data/spec/babushka/dep_context_spec.rb +58 -0
  123. data/spec/babushka/dep_definer_spec.rb +152 -0
  124. data/spec/babushka/dep_definer_support.rb +36 -0
  125. data/spec/babushka/dep_spec.rb +567 -0
  126. data/spec/babushka/dep_support.rb +29 -0
  127. data/spec/babushka/deps_spec.rb +113 -0
  128. data/spec/babushka/gem_helper_spec.rb +90 -0
  129. data/spec/babushka/git_repo_spec.rb +396 -0
  130. data/spec/babushka/ip_spec.rb +131 -0
  131. data/spec/babushka/lambda_chooser_spec.rb +115 -0
  132. data/spec/babushka/meta_dep_definer_spec.rb +127 -0
  133. data/spec/babushka/meta_dep_wrapper_spec.rb +32 -0
  134. data/spec/babushka/parameter_spec.rb +135 -0
  135. data/spec/babushka/path_helpers_spec.rb +102 -0
  136. data/spec/babushka/prompt_spec.rb +188 -0
  137. data/spec/babushka/renderable_spec.rb +100 -0
  138. data/spec/babushka/resource_spec.rb +141 -0
  139. data/spec/babushka/run_helpers_spec.rb +26 -0
  140. data/spec/babushka/shell_helpers_spec.rb +244 -0
  141. data/spec/babushka/shell_spec.rb +19 -0
  142. data/spec/babushka/source_pool_spec.rb +320 -0
  143. data/spec/babushka/source_pool_support.rb +31 -0
  144. data/spec/babushka/source_spec.rb +382 -0
  145. data/spec/babushka/source_support.rb +17 -0
  146. data/spec/babushka/system_profile_spec.rb +61 -0
  147. data/spec/babushka/task_spec.rb +141 -0
  148. data/spec/babushka/uri_spec.rb +13 -0
  149. data/spec/babushka/vars_spec.rb +59 -0
  150. data/spec/babushka/version_of_spec.rb +110 -0
  151. data/spec/babushka/version_str_spec.rb +130 -0
  152. data/spec/babushka/version_str_support.rb +37 -0
  153. data/spec/babushka/xml_string_spec.rb +98 -0
  154. data/spec/deps/bad/broken.rb +7 -0
  155. data/spec/deps/bad/working.rb +3 -0
  156. data/spec/deps/good/meta.rb +14 -0
  157. data/spec/deps/good/test.rb +11 -0
  158. data/spec/deps/outer/deps.rb +19 -0
  159. data/spec/deps/outer/more deps.rb +11 -0
  160. data/spec/deps/params/params.rb +10 -0
  161. data/spec/fancypath/fancypath_spec.rb +272 -0
  162. data/spec/fancypath_support.rb +10 -0
  163. data/spec/inkan/inkan_spec.rb +217 -0
  164. data/spec/renderable/different_example.conf.erb +4 -0
  165. data/spec/renderable/example.conf.erb +3 -0
  166. data/spec/renderable/example.sh +6 -0
  167. data/spec/renderable/with_binding.conf.erb +4 -0
  168. data/spec/renderable/xml_example.conf.erb +8 -0
  169. data/spec/repos/remote.git.tgz +0 -0
  170. data/spec/spec_helper.rb +87 -0
  171. metadata +238 -0
@@ -0,0 +1,210 @@
1
+ module Babushka
2
+ class SystemProfile
3
+ include ShellHelpers
4
+ extend ShellHelpers
5
+
6
+ def self.for_host
7
+ {
8
+ 'Linux' => LinuxSystemProfile,
9
+ 'Darwin' => OSXSystemProfile,
10
+ 'DragonFly' => DragonFlySystemProfile,
11
+ 'FreeBSD' => FreeBSDSystemProfile
12
+ }[shell('uname -s')].try(:for_flavour)
13
+ end
14
+
15
+ def self.for_flavour
16
+ new
17
+ end
18
+
19
+ def version_info
20
+ @_version_info ||= get_version_info
21
+ end
22
+
23
+ def linux?; false end
24
+ def osx?; false end
25
+ def bsd?; false end
26
+ def pkg_helper; nil end
27
+ def pkg_helper_key; pkg_helper.try(:manager_key) end
28
+ def pkg_helper_str; pkg_helper_key.to_s.capitalize end
29
+ # The extension that dynamic libraries are given on this system. On linux
30
+ # libraries are named like 'libssl.so'; on OS X, 'libssl.bundle'.
31
+ def library_ext; 'so' end
32
+
33
+ def cpu_type
34
+ shell('uname -p').tap {|result|
35
+ result.replace shell('uname -m') if result[/unknown|\s/]
36
+ # These replacements are taken from PhusionPassenger::PlatformInfo.cpu_architectures
37
+ result.replace 'x86' if result[/^i.86$/]
38
+ result.replace 'x86_64' if result == 'amd64'
39
+ }
40
+ end
41
+
42
+ def total_memory
43
+ raise "#{self.class}#total_memory is unimplemented."
44
+ end
45
+
46
+ def description
47
+ [
48
+ (flavour_str unless flavour_str == system_str),
49
+ system_str,
50
+ version,
51
+ ("(#{name_str})" unless name_str.nil?)
52
+ ].compact.join(' ')
53
+ end
54
+
55
+ def name
56
+ (SystemDefinitions.names[system][flavour] || {})[release]
57
+ end
58
+ def name_str
59
+ (SystemDefinitions.descriptions[system][flavour] || {})[release]
60
+ end
61
+
62
+ def match_list
63
+ [name, flavour, pkg_helper_key, system, :all].compact
64
+ end
65
+
66
+ def matches? specs
67
+ [*specs].any? {|spec| first_nonmatch_for(spec).nil? }
68
+ end
69
+
70
+ def first_nonmatch_for spec
71
+ if spec == :all
72
+ nil
73
+ elsif SystemDefinitions.all_systems.include? spec
74
+ spec == system ? nil : :system
75
+ elsif PkgHelper.all_manager_keys.include? spec
76
+ spec == pkg_helper_key ? nil : :pkg_helper
77
+ elsif our_flavours.include? spec
78
+ spec == flavour ? nil : :flavour
79
+ elsif our_flavour_names.include? spec
80
+ spec == name ? nil : :name
81
+ else
82
+ :system
83
+ end
84
+ end
85
+
86
+ def differentiator_for specs
87
+ nonmatches = [*specs].map {|spec|
88
+ first_nonmatch_for spec
89
+ }.sort_by {|spec|
90
+ [:system, :flavour, :name].index spec
91
+ }.compact
92
+ send "#{nonmatches.last}_str" unless nonmatches.empty?
93
+ end
94
+
95
+ def our_flavours
96
+ SystemDefinitions.names[system].keys
97
+ end
98
+ def our_flavour_names
99
+ SystemDefinitions.names[system][flavour].values
100
+ end
101
+
102
+ def flavour_str_map
103
+ # Only required for names that can't be auto-capitalized,
104
+ # e.g. :ubuntu => 'Ubuntu' isn't required.
105
+ {
106
+ :linux => {
107
+ :centos => 'CentOS',
108
+ :redhat => 'Red Hat'
109
+ }
110
+ }
111
+ end
112
+
113
+ end
114
+
115
+ class OSXSystemProfile < SystemProfile
116
+ def osx?; true end
117
+ def library_ext; 'bundle' end
118
+ def system; :osx end
119
+ def system_str; 'Mac OS X' end
120
+ def flavour; system end
121
+ def flavour_str; system_str end
122
+ def version; version_info.val_for 'ProductVersion' end
123
+ def release; version.match(/^\d+\.\d+/).to_s end
124
+ def get_version_info; shell 'sw_vers' end
125
+ def pkg_helper; BrewHelper end
126
+ def total_memory; shell("sysctl -a").val_for("hw.memsize").to_i end
127
+ end
128
+
129
+ class BSDSystemProfile < SystemProfile
130
+ def bsd?; true end
131
+ def system; :bsd end
132
+ def system_str; 'BSD' end
133
+ def flavour; :unknown end
134
+ def flavour_str; system_str end
135
+ def version; shell 'uname -s' end
136
+ def release; shell 'uname -r' end
137
+ def get_version_info; shell 'uname -v' end
138
+ end
139
+
140
+ class DragonFlySystemProfile < BSDSystemProfile
141
+ def system_str; 'DragonFly' end
142
+ def flavour; :dragonfly end
143
+ def pkg_helper; BinPkgSrcHelper end
144
+ def total_memory; shell("sysctl -a").val_for("hw.physmem").to_i end
145
+ end
146
+
147
+ class FreeBSDSystemProfile < BSDSystemProfile
148
+ def system_str; 'FreeBSD' end
149
+ def flavour; :freebsd end
150
+ def pkg_helper; BinPortsHelper end
151
+ def total_memory; shell("sysctl -a").val_for("hw.realmem").to_i end
152
+ end
153
+
154
+ class LinuxSystemProfile < SystemProfile
155
+ def linux?; true end
156
+ def system; :linux end
157
+ def system_str; 'Linux' end
158
+ def flavour; :unknown end
159
+ def flavour_str; flavour_str_map[system][flavour] end
160
+ def version; 'unknown' end
161
+ def release; version end
162
+
163
+ def self.for_flavour
164
+ (detect_using_release_file || LinuxSystemProfile).new
165
+ end
166
+
167
+ private
168
+
169
+ def self.detect_using_release_file
170
+ {
171
+ 'debian_version' => DebianSystemProfile,
172
+ 'redhat-release' => RedhatSystemProfile,
173
+ 'arch-release' => ArchSystemProfile,
174
+ # 'gentoo-release' =>
175
+ # 'SuSE-release' =>
176
+ }.selekt {|release_file, system_profile|
177
+ File.exists? "/etc/#{release_file}"
178
+ }.values.first
179
+ end
180
+ end
181
+
182
+ class DebianSystemProfile < LinuxSystemProfile
183
+ def flavour; flavour_str.downcase.to_sym end
184
+ def flavour_str; version_info.val_for 'Distributor ID' end
185
+ def version; version_info.val_for 'Release' end
186
+ def name; version_info.val_for('Codename').to_sym end
187
+ def get_version_info; ensure_lsb_release and shell('lsb_release -a') end
188
+ def ensure_lsb_release
189
+ which('lsb_release') or log("Babushka uses `lsb_release` to learn about debian-based systems.") {
190
+ AptHelper.install!('lsb-release')
191
+ }
192
+ end
193
+ def pkg_helper; AptHelper end
194
+ def total_memory; shell("free -b").val_for("Mem").scan(/^\d+\b/).first.to_i end
195
+ end
196
+
197
+ class RedhatSystemProfile < LinuxSystemProfile
198
+ def flavour; version_info[/^Red Hat/i] ? :redhat : version_info[/^\w+/].downcase.to_sym end
199
+ def version; version_info[/release [\d\.]+ \((\w+)\)/i, 1] || version_info[/release ([\d\.]+)/i, 1] end
200
+ def get_version_info; File.read '/etc/redhat-release' end
201
+ def pkg_helper; YumHelper end
202
+ end
203
+
204
+ class ArchSystemProfile < LinuxSystemProfile
205
+ def get_version_info; 'rolling' end
206
+ def pkg_helper; PacmanHelper end
207
+ def flavour; :arch end
208
+ def version; ''; end
209
+ end
210
+ end
@@ -0,0 +1,142 @@
1
+ module Babushka
2
+ class Task
3
+ include Singleton
4
+ include LogHelpers
5
+ include PathHelpers
6
+ include ShellHelpers
7
+
8
+ attr_reader :opts, :vars, :persistent_log
9
+ attr_accessor :reportable
10
+
11
+ def initialize
12
+ @vars = Vars.new
13
+ @opts = Base.cmdline.opts.dup
14
+ @running = false
15
+ end
16
+
17
+ def process dep_names, with_vars = {}
18
+ raise "A task is already running." if running?
19
+ @running = true
20
+ Base.in_thread { RunReporter.post_reports }
21
+ dep_names.all? {|dep_name| process_dep dep_name, with_vars }
22
+ ensure
23
+ @running = false
24
+ end
25
+
26
+ def process_dep dep_name, with_vars
27
+ Dep.find_or_suggest dep_name do |dep|
28
+ log_dep(dep) {
29
+ load_run_info_for dep, with_vars
30
+ dep.with(task_args_for(dep, with_vars)).process(:top_level => true).tap {|result|
31
+ save_run_info_for dep, result
32
+ }
33
+ }.tap {|result|
34
+ log "You can view #{opt(:debug) ? 'the' : 'a more detailed'} log at '#{log_path_for(dep)}'." unless result
35
+ RunReporter.queue dep, result, reportable
36
+ BugReporter.report dep if reportable
37
+ }
38
+ end
39
+ end
40
+
41
+ def task_info dep, result
42
+ {
43
+ :version => VERSION,
44
+ :run_at => Time.now,
45
+ :system_info => Base.host.description,
46
+ :dep_name => dep.name,
47
+ :source_uri => dep.dep_source.uri,
48
+ :result => result
49
+ }
50
+ end
51
+
52
+ def opt name
53
+ opts[name]
54
+ end
55
+
56
+ def running?
57
+ @running
58
+ end
59
+
60
+ def callstack
61
+ @callstack ||= []
62
+ end
63
+
64
+ def log_path_for dep
65
+ log_prefix / dep.contextual_name
66
+ end
67
+
68
+ def var_path_for dep
69
+ VarsPrefix.p / dep.contextual_name
70
+ end
71
+
72
+ private
73
+
74
+ def task_args_for dep, with_vars
75
+ with_vars.keys.inject({}) {|hsh,k|
76
+ # The string arg names are sanitized in the 'meet' cmdline handler.
77
+ hsh[k.to_sym] = with_vars[k]; hsh
78
+ }.tap {|with_args|
79
+ if (unexpected = with_args.keys - dep.params).any?
80
+ log_warn "Ignoring unexpected argument#{'s' if unexpected.length > 1} #{unexpected.map(&:to_s).map(&:inspect).to_list}, which the dep '#{dep.name}' would reject."
81
+ unexpected.each {|key| with_args.delete(key) }
82
+ end
83
+ }
84
+ end
85
+
86
+ def log_dep dep
87
+ log_prefix.mkdir
88
+ log_path_for(dep).open('w') {|f|
89
+ f.sync = true
90
+ @persistent_log = f
91
+ yield.tap { @persistent_log = nil }
92
+ }
93
+ end
94
+
95
+ def log_prefix
96
+ LogPrefix.p
97
+ end
98
+
99
+ def load_run_info_for dep, with_vars
100
+ load_var_log_for(var_path_for(dep)).each_pair {|var_name,var_data|
101
+ vars.saved_vars[var_name].update var_data
102
+ }
103
+ with_vars.each_pair {|var_name,var_value|
104
+ vars.vars[var_name].update :value => var_value
105
+ }
106
+ end
107
+
108
+ def save_run_info_for dep, result
109
+ save_var_log_for var_path_for(dep), {
110
+ :info => task_info(dep, result),
111
+ :vars => vars.for_save
112
+ }
113
+ end
114
+
115
+ def load_var_log_for path
116
+ require 'yaml'
117
+ unless File.exists? path
118
+ debug "No log to load for '#{path}'."
119
+ else
120
+ dep_log = YAML.load_file path
121
+ unless dep_log.is_a?(Hash) && dep_log[:vars].is_a?(Hash)
122
+ log_error "Ignoring corrupt var log at #{path}."
123
+ else
124
+ dep_log[:vars]
125
+ end
126
+ end || {}
127
+ end
128
+
129
+ def save_var_log_for var_path, data
130
+ cd File.dirname(var_path), :create => true do |path|
131
+ debug "Saving #{var_path}"
132
+ dump_yaml_to File.basename(var_path), data
133
+ end
134
+ end
135
+
136
+ def dump_yaml_to filename, data
137
+ require 'yaml'
138
+ File.open(filename, 'w') {|f| YAML.dump data, f }
139
+ end
140
+
141
+ end
142
+ end
@@ -0,0 +1,108 @@
1
+ module Babushka
2
+ class Vars
3
+ include LogHelpers
4
+
5
+ attr_reader :vars, :saved_vars
6
+
7
+ module Helpers
8
+ def set(key, value) Base.task.vars.set(key, value) end
9
+ def merge(key, value) Base.task.vars.merge(key, value) end
10
+ def var(name, opts = {}) Base.task.vars.var(name, opts) end
11
+ def define_var(name, opts = {}) Base.task.vars.define_var(name, opts) end
12
+ end
13
+
14
+ def initialize
15
+ @vars = Hashish.hash
16
+ @saved_vars = Hashish.hash
17
+ end
18
+
19
+ def set key, value
20
+ vars[key.to_s][:value] = value
21
+ end
22
+ def merge key, value
23
+ set key, ((vars[key.to_s] || {})[:value] || {}).merge(value)
24
+ end
25
+
26
+ def var name, opts = {}
27
+ define_var name, opts
28
+ if vars[name.to_s].has_key? :value
29
+ if vars[name.to_s][:value].respond_to? :call
30
+ vars[name.to_s][:value].call
31
+ else
32
+ vars[name.to_s][:value]
33
+ end
34
+ elsif opts[:ask] != false
35
+ ask_for_var name.to_s, opts
36
+ else
37
+ default_for name
38
+ end
39
+ end
40
+
41
+ def define_var name, opts = {}
42
+ vars[name.to_s].update opts.slice(:default, :type, :message, :choices, :choice_descriptions)
43
+ vars[name.to_s][:choices] ||= vars[name.to_s][:choice_descriptions].keys unless vars[name.to_s][:choice_descriptions].nil?
44
+ vars[name.to_s]
45
+ end
46
+
47
+ def for_save
48
+ vars.dup.inject(saved_vars.dup) {|vars_to_save,(var,_)|
49
+ vars_to_save[var].update vars[var]
50
+ save_referenced_default_for(var, vars_to_save) if vars[var][:default].is_a?(Symbol)
51
+ vars_to_save
52
+ }.reject_r {|var,data|
53
+ ![String, Symbol, Hash, Numeric, TrueClass, FalseClass].include?(data.class) ||
54
+ var.to_s['password']
55
+ }
56
+ end
57
+
58
+ def default_for key
59
+ if vars[key.to_s][:default].respond_to? :call
60
+ # If the default is a proc, re-evaluate it every time.
61
+ instance_eval { vars[key.to_s][:default].call }
62
+
63
+ elsif saved_vars[key.to_s].has_key? :value
64
+ # Otherwise, if there's a saved value, use that.
65
+ saved_vars[key.to_s][:value]
66
+
67
+ # Symbol defaults are references to other vars.
68
+ elsif vars[key.to_s][:default].is_a? Symbol
69
+ # Look up the current value of the referenced var.
70
+ referenced_val = var vars[key.to_s][:default], :ask => false
71
+ # Use the corresponding saved value if there is one, otherwise use the reference.
72
+ (saved_vars[key.to_s][:values] ||= {})[referenced_val] || referenced_val
73
+
74
+ else
75
+ # Otherwise, use the default.
76
+ vars[key.to_s][:default]
77
+ end
78
+ end
79
+
80
+
81
+ private
82
+
83
+ def ask_for_var key, opts
84
+ set key, Prompt.send("get_#{vars[key][:type] || 'value'}",
85
+ message_for(key),
86
+ vars[key].slice(:choices, :choice_descriptions).merge(
87
+ opts
88
+ ).merge(
89
+ :default => default_for(key),
90
+ :dynamic => vars[key][:default].respond_to?(:call)
91
+ )
92
+ )
93
+ end
94
+
95
+ def message_for key
96
+ printable_key = key.to_s.gsub '_', ' '
97
+ vars[key][:message] || printable_key
98
+ end
99
+
100
+ def save_referenced_default_for var, vars_to_save
101
+ vars_to_save[var][:values] ||= {}
102
+ vars_to_save[var][:values][ # set the saved value of this var
103
+ vars[vars[var][:default].to_s][:value] # for this var's current default reference
104
+ ] = vars_to_save[var].delete(:value) # to the referenced var's value
105
+ end
106
+
107
+ end
108
+ end