linecook 0.6.2

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 (47) hide show
  1. data/History +60 -0
  2. data/License.txt +22 -0
  3. data/README +98 -0
  4. data/bin/linecook +58 -0
  5. data/cookbook +0 -0
  6. data/lib/linecook/attributes.rb +22 -0
  7. data/lib/linecook/commands/command.rb +48 -0
  8. data/lib/linecook/commands/command_error.rb +6 -0
  9. data/lib/linecook/commands/env.rb +23 -0
  10. data/lib/linecook/commands/helper.rb +51 -0
  11. data/lib/linecook/commands/helpers.rb +28 -0
  12. data/lib/linecook/commands/init.rb +82 -0
  13. data/lib/linecook/commands/package.rb +39 -0
  14. data/lib/linecook/commands/vbox.rb +85 -0
  15. data/lib/linecook/commands.rb +6 -0
  16. data/lib/linecook/cookbook.rb +104 -0
  17. data/lib/linecook/helper.rb +117 -0
  18. data/lib/linecook/package.rb +197 -0
  19. data/lib/linecook/recipe.rb +103 -0
  20. data/lib/linecook/shell/posix.rb +145 -0
  21. data/lib/linecook/shell/test.rb +254 -0
  22. data/lib/linecook/shell/unix.rb +117 -0
  23. data/lib/linecook/shell/utils.rb +138 -0
  24. data/lib/linecook/shell.rb +11 -0
  25. data/lib/linecook/template.rb +111 -0
  26. data/lib/linecook/test/file_test.rb +77 -0
  27. data/lib/linecook/test/regexp_escape.rb +86 -0
  28. data/lib/linecook/test.rb +172 -0
  29. data/lib/linecook/utils.rb +53 -0
  30. data/lib/linecook/version.rb +8 -0
  31. data/lib/linecook.rb +6 -0
  32. data/templates/Gemfile +2 -0
  33. data/templates/README +90 -0
  34. data/templates/Rakefile +149 -0
  35. data/templates/_gitignore +5 -0
  36. data/templates/attributes/project_name.rb +4 -0
  37. data/templates/cookbook +9 -0
  38. data/templates/files/file.txt +1 -0
  39. data/templates/helpers/project_name/echo.erb +5 -0
  40. data/templates/project_name.gemspec +30 -0
  41. data/templates/recipes/project_name.rb +20 -0
  42. data/templates/scripts/project_name.yml +7 -0
  43. data/templates/templates/template.txt.erb +3 -0
  44. data/templates/vbox/setup/virtual_box +86 -0
  45. data/templates/vbox/ssh/id_rsa +27 -0
  46. data/templates/vbox/ssh/id_rsa.pub +1 -0
  47. metadata +166 -0
@@ -0,0 +1,254 @@
1
+ require 'linecook/test'
2
+
3
+ module Linecook
4
+ module Shell
5
+ # A module for testing shell scripts.
6
+ #
7
+ # class ShellTestSample < Test::Unit::TestCase
8
+ # include Linecook::Test::Shell
9
+ #
10
+ # # these are the default sh_test options used
11
+ # # in tests like test_sh_command_alias
12
+ # self.sh_test_options = {
13
+ # :cmd_pattern => '% inspect_argv',
14
+ # :cmd => 'ruby -e "puts ARGV.inspect"'
15
+ # }
16
+ #
17
+ # def test_echo
18
+ # assert_equal "goodnight moon", sh("echo goodnight moon").strip
19
+ # end
20
+ #
21
+ # def test_echo_using_sh_test
22
+ # sh_test %q{
23
+ # echo goodnight moon
24
+ # goodnight moon
25
+ # }
26
+ # end
27
+ #
28
+ # def test_sh_command_alias
29
+ # sh_test("% inspect_env") do |output|
30
+ # assert output !~ /NEW_ENV_VAR/
31
+ # end
32
+ #
33
+ # sh_test("NEW_ENV_VAR=blue % inspect_env") do |output|
34
+ # assert output =~ /NEW_ENV_VAR=blue/
35
+ # end
36
+ # end
37
+ # end
38
+ #
39
+ module Test
40
+ include Linecook::Test
41
+
42
+ def setup
43
+ super
44
+ @notify_method_name = true
45
+ end
46
+
47
+ # Sets the specified ENV variables and returns the *current* env.
48
+ # If replace is true, current ENV variables are replaced; otherwise
49
+ # the new env variables are simply added to the existing set.
50
+ def set_env(env={}, replace=false)
51
+ current_env = {}
52
+ ENV.each_pair do |key, value|
53
+ current_env[key] = value
54
+ end
55
+
56
+ ENV.clear if replace
57
+
58
+ env.each_pair do |key, value|
59
+ if value.nil?
60
+ ENV.delete(key)
61
+ else
62
+ ENV[key] = value
63
+ end
64
+ end if env
65
+
66
+ current_env
67
+ end
68
+
69
+ # Sets the specified ENV variables for the duration of the block.
70
+ # If replace is true, current ENV variables are replaced; otherwise
71
+ # the new env variables are simply added to the existing set.
72
+ #
73
+ # Returns the block return.
74
+ def with_env(env={}, replace=false)
75
+ current_env = nil
76
+ begin
77
+ current_env = set_env(env, replace)
78
+ yield
79
+ ensure
80
+ if current_env
81
+ set_env(current_env, true)
82
+ end
83
+ end
84
+ end
85
+
86
+ # Returns true if the ENV variable 'VERBOSE' is true.
87
+ def verbose?
88
+ verbose = ENV['VERBOSE']
89
+ verbose && verbose =~ /^true$/i ? true : false
90
+ end
91
+
92
+ # Returns true if the ENV variable 'QUIET' is true or nil. If 'VERBOSE'
93
+ # and 'QUIET' are both set, verbose wins.
94
+ def quiet?
95
+ return false if verbose?
96
+
97
+ quiet = ENV['QUIET']
98
+ quiet.nil? || quiet =~ /^true$/i ? true : false
99
+ end
100
+
101
+ # Executes the command using IO.popen and returns the stdout content.
102
+ #
103
+ # ==== Note
104
+ # On Windows this method requires the {win32-popen3}[http://rubyforge.org/projects/win32utils]
105
+ # utility. If it is not available, it will have to be installed:
106
+ #
107
+ # % gem install win32-open3
108
+ #
109
+ def sh(cmd, options={})
110
+ if @notify_method_name && !quiet?
111
+ @notify_method_name = false
112
+ puts
113
+ puts method_name
114
+ end
115
+
116
+ original_cmd = cmd
117
+ if cmd_pattern = options[:cmd_pattern]
118
+ cmd = cmd.sub(cmd_pattern, options[:cmd].to_s)
119
+ end
120
+
121
+ start = Time.now
122
+ result = with_env(options[:env], options[:replace_env]) do
123
+ IO.popen(cmd) do |io|
124
+ yield(io) if block_given?
125
+ io.read
126
+ end
127
+ end
128
+
129
+ finish = Time.now
130
+ elapsed = "%.3f" % [finish-start]
131
+ puts " (#{elapsed}s) #{verbose? ? cmd : original_cmd}" unless quiet?
132
+ result
133
+ end
134
+
135
+ # Peforms a shell test. Shell tests execute the command and yield the
136
+ # $stdout result to the block for validation. The command is executed
137
+ # through sh, ie using IO.popen.
138
+ #
139
+ # Options provided to sh_test are merged with the sh_test_options set
140
+ # for the class.
141
+ #
142
+ # ==== Command Aliases
143
+ #
144
+ # The options allow specification of a command pattern that gets
145
+ # replaced with a command alias. Only the first instance of the command
146
+ # pattern is replaced. In addition, shell tests allow the expected result
147
+ # to be specified inline with the command. Used together, these allow
148
+ # multiple tests of a complex command to be specified easily:
149
+ #
150
+ # opts = {
151
+ # :cmd_pattern => '% argv_inspect',
152
+ # :cmd => 'ruby -e "puts ARGV.inspect"'
153
+ # }
154
+ #
155
+ # sh_test %Q{
156
+ # % argv_inspect goodnight moon
157
+ # ["goodnight", "moon"]
158
+ # }, opts
159
+ #
160
+ # sh_test %Q{
161
+ # % argv_inspect hello world
162
+ # ["hello", "world"]
163
+ # }, opts
164
+ #
165
+ # ==== Indents
166
+ #
167
+ # To improve the readability of tests, sh_test will lstrip each line in the
168
+ # expected output to the same degree as the command line. So for instance
169
+ # these all pass:
170
+ #
171
+ # sh_test %Q{
172
+ # % argv_inspect hello world
173
+ # ["hello", "world"]
174
+ # }, opts
175
+ #
176
+ # sh_test %Q{
177
+ # % argv_inspect hello world
178
+ # ["hello", "world"]
179
+ # }, opts
180
+ #
181
+ # sh_test %Q{
182
+ # % argv_inspect hello world
183
+ # ["hello", "world"]
184
+ # }, opts
185
+ #
186
+ # Turn off indent stripping by specifying :indent => false.
187
+ #
188
+ # ==== ENV variables
189
+ #
190
+ # Options may specify a hash of env variables that will be set in the
191
+ # subprocess.
192
+ #
193
+ # sh_test %Q{
194
+ # ruby -e "puts ENV['SAMPLE']"
195
+ # value
196
+ # }, :env => {'SAMPLE' => 'value'}
197
+ #
198
+ # Note it is better to specify env variables in this way rather than
199
+ # through the command trick 'VAR=value cmd ...', as that syntax does
200
+ # not work on Windows. As a point of interest, see
201
+ # http://gist.github.com/107363 for a demonstration of ENV
202
+ # variables being inherited by subprocesses.
203
+ #
204
+ def sh_test(cmd, options={})
205
+ options = sh_test_options.merge(options)
206
+
207
+ # strip indentiation if possible
208
+ if cmd =~ /\A(?:\s*?\n)?( *)(.*?\n)(.*)\z/m
209
+ indent, cmd, expected = $1, $2, $3
210
+ cmd.strip!
211
+
212
+ if indent.length > 0 && options[:indents]
213
+ expected.gsub!(/^ {0,#{indent.length}}/, '')
214
+ end
215
+ end
216
+
217
+ result = sh(cmd, options)
218
+
219
+ assert_equal(expected, result, cmd) if expected
220
+ yield(result) if block_given?
221
+ result
222
+ end
223
+
224
+ # Similar to sh_test, but matches the output against each of the
225
+ # regexps. A hash of sh options can be provided as the last argument;
226
+ # it will be merged with the default sh_test_options.
227
+ #
228
+ # The output is yielded to the block, if given, for further validation.
229
+ # Returns the sh output.
230
+ def sh_match(cmd, *regexps)
231
+ options = regexps.last.kind_of?(Hash) ? regexps.pop : {}
232
+ options = sh_test_options.merge(options)
233
+ result = sh(cmd, options)
234
+
235
+ regexps.each do |regexp|
236
+ assert_match regexp, result, cmd
237
+ end
238
+ yield(result) if block_given?
239
+ result
240
+ end
241
+
242
+ # Returns a hash of default sh_test options.
243
+ def sh_test_options
244
+ {
245
+ :cmd_pattern => '% ',
246
+ :cmd => '2>&1 ',
247
+ :indents => true,
248
+ :env => {},
249
+ :replace_env => false
250
+ }
251
+ end
252
+ end
253
+ end
254
+ end
@@ -0,0 +1,117 @@
1
+ require 'erb'
2
+
3
+ # Generated by Linecook, do not edit.
4
+ module Linecook
5
+ module Shell
6
+ module Unix
7
+ require 'linecook/shell/posix'
8
+ include Posix
9
+ # :stopdoc:
10
+ CAT_LINE = __LINE__ + 2
11
+ CAT = "self." + ERB.new(<<'END_OF_TEMPLATE', nil, '<>').src
12
+ cat<% sources.each do |source| %> "<%= source %>"<% end %>
13
+ END_OF_TEMPLATE
14
+ # :startdoc:
15
+
16
+ def cat(*sources)
17
+ eval(CAT, binding, __FILE__, CAT_LINE)
18
+ nil
19
+ end
20
+
21
+ def _cat(*args, &block) # :nodoc:
22
+ capture { cat(*args, &block) }
23
+ end
24
+
25
+ # :stopdoc:
26
+ CHMOD_LINE = __LINE__ + 2
27
+ CHMOD = "self." + ERB.new(<<'END_OF_TEMPLATE', nil, '<>').src
28
+ <% if mode %>
29
+ chmod <%= mode %> "<%= target %>"
30
+ <% check_status %>
31
+ <% end %>
32
+ END_OF_TEMPLATE
33
+ # :startdoc:
34
+
35
+ def chmod(target, mode=nil)
36
+ eval(CHMOD, binding, __FILE__, CHMOD_LINE)
37
+ nil
38
+ end
39
+
40
+ def _chmod(*args, &block) # :nodoc:
41
+ capture { chmod(*args, &block) }
42
+ end
43
+
44
+ # :stopdoc:
45
+ CHOWN_LINE = __LINE__ + 2
46
+ CHOWN = "self." + ERB.new(<<'END_OF_TEMPLATE', nil, '<>').src
47
+ <% if user || group %>
48
+ chown <%= user %>:<%= group %> "<%= target %>"
49
+ <% check_status %>
50
+ <% end %>
51
+ END_OF_TEMPLATE
52
+ # :startdoc:
53
+
54
+ def chown(target, user=nil, group=nil)
55
+ eval(CHOWN, binding, __FILE__, CHOWN_LINE)
56
+ nil
57
+ end
58
+
59
+ def _chown(*args, &block) # :nodoc:
60
+ capture { chown(*args, &block) }
61
+ end
62
+
63
+ # :stopdoc:
64
+ ECHO_LINE = __LINE__ + 2
65
+ ECHO = "self." + ERB.new(<<'END_OF_TEMPLATE', nil, '<>').src
66
+ echo '<%= args.join(" ") %>'
67
+ END_OF_TEMPLATE
68
+ # :startdoc:
69
+
70
+ # Echos input
71
+ def echo(*args)
72
+ eval(ECHO, binding, __FILE__, ECHO_LINE)
73
+ nil
74
+ end
75
+
76
+ def _echo(*args, &block) # :nodoc:
77
+ capture { echo(*args, &block) }
78
+ end
79
+
80
+ # :stopdoc:
81
+ LN_S_LINE = __LINE__ + 2
82
+ LN_S = "self." + ERB.new(<<'END_OF_TEMPLATE', nil, '<>').src
83
+ ln -sf "<%= source %>" "<%= target %>"
84
+ <% check_status %>
85
+
86
+ END_OF_TEMPLATE
87
+ # :startdoc:
88
+
89
+ def ln_s(source, target)
90
+ eval(LN_S, binding, __FILE__, LN_S_LINE)
91
+ nil
92
+ end
93
+
94
+ def _ln_s(*args, &block) # :nodoc:
95
+ capture { ln_s(*args, &block) }
96
+ end
97
+
98
+ # :stopdoc:
99
+ RM_LINE = __LINE__ + 2
100
+ RM = "self." + ERB.new(<<'END_OF_TEMPLATE', nil, '<>').src
101
+ <% only_if %Q{ls -l "#{path}"} do %>
102
+ rm <% if opts %><%= opts %> <% end %>"<%= path %>"
103
+ <% end %>
104
+ END_OF_TEMPLATE
105
+ # :startdoc:
106
+
107
+ def rm(path, opts=nil)
108
+ eval(RM, binding, __FILE__, RM_LINE)
109
+ nil
110
+ end
111
+
112
+ def _rm(*args, &block) # :nodoc:
113
+ capture { rm(*args, &block) }
114
+ end
115
+ end
116
+ end
117
+ end
@@ -0,0 +1,138 @@
1
+ require 'erb'
2
+
3
+ # Generated by Linecook, do not edit.
4
+ module Linecook
5
+ module Shell
6
+ module Utils
7
+ require 'linecook/shell/posix'
8
+ include Posix
9
+
10
+ DEFAULT_SHELL_PATH = '/bin/sh'
11
+ DEFAULT_ENV_PATH = '/usr/bin/env'
12
+
13
+ TARGET_PATH = '$LINECOOK_DIR/%s'
14
+
15
+ attr_writer :shell_path
16
+ attr_writer :env_path
17
+
18
+ def shell_path
19
+ @shell_path ||= DEFAULT_SHELL_PATH
20
+ end
21
+
22
+ def env_path
23
+ @env_path ||= DEFAULT_ENV_PATH
24
+ end
25
+
26
+ def target_path(source_path)
27
+ TARGET_PATH % super(source_path)
28
+ end
29
+
30
+ def close
31
+ unless closed?
32
+ break_line " (#{target_name}) "
33
+ end
34
+
35
+ super
36
+ end
37
+ # :stopdoc:
38
+ BREAK_LINE_LINE = __LINE__ + 2
39
+ BREAK_LINE = "self." + ERB.new(<<'END_OF_TEMPLATE', nil, '<>').src
40
+ <% n = (76 - comment.length)/2 %>
41
+ <%= "#" * n %><%= comment %><%= "#" * n %>
42
+
43
+ END_OF_TEMPLATE
44
+ # :startdoc:
45
+
46
+ def break_line(comment="")
47
+ eval(BREAK_LINE, binding, __FILE__, BREAK_LINE_LINE)
48
+ nil
49
+ end
50
+
51
+ def _break_line(*args, &block) # :nodoc:
52
+ capture { break_line(*args, &block) }
53
+ end
54
+
55
+ # :stopdoc:
56
+ CHECK_STATUS_LINE = __LINE__ + 2
57
+ CHECK_STATUS = "self." + ERB.new(<<'END_OF_TEMPLATE', nil, '<>').src
58
+ check_status <%= status %> $? $LINENO
59
+ END_OF_TEMPLATE
60
+ # :startdoc:
61
+
62
+ # Adds a check after a command that ensures the status is as indicated
63
+ def check_status(status=0)
64
+ eval(CHECK_STATUS, binding, __FILE__, CHECK_STATUS_LINE)
65
+ nil
66
+ end
67
+
68
+ def _check_status(*args, &block) # :nodoc:
69
+ capture { check_status(*args, &block) }
70
+ end
71
+
72
+ # :stopdoc:
73
+ CHECK_STATUS_FUNCTION_LINE = __LINE__ + 2
74
+ CHECK_STATUS_FUNCTION = "self." + ERB.new(<<'END_OF_TEMPLATE', nil, '<>').src
75
+ function check_status { if [ $1 -ne $2 ]; then echo "[$2] $0:$3"; exit $2; fi }
76
+ END_OF_TEMPLATE
77
+ # :startdoc:
78
+
79
+ # Adds the check status function.
80
+ def check_status_function
81
+ eval(CHECK_STATUS_FUNCTION, binding, __FILE__, CHECK_STATUS_FUNCTION_LINE)
82
+ nil
83
+ end
84
+
85
+ def _check_status_function(*args, &block) # :nodoc:
86
+ capture { check_status_function(*args, &block) }
87
+ end
88
+
89
+ # :stopdoc:
90
+ SHEBANG_LINE = __LINE__ + 2
91
+ SHEBANG = "self." + ERB.new(<<'END_OF_TEMPLATE', nil, '<>').src
92
+ #! <%= shell_path %>
93
+
94
+ <%= break_line %>
95
+ <%= check_status_function %>
96
+
97
+ export -f check_status
98
+ export LINECOOK_DIR=$(dirname $0)
99
+ export LINECOOK_OPTIONS=
100
+
101
+ while getopts bhvx opt
102
+ do
103
+ case $opt in
104
+ v) LINECOOK_OPTIONS="$LINECOOK_OPTIONS -v";;
105
+ x) LINECOOK_OPTIONS="$LINECOOK_OPTIONS -x";;
106
+ h) printf "Usage: %s: [-hvx]\n" $0
107
+ printf " -h prints this help\n"
108
+ printf " -v verbose (set -v)\n"
109
+ printf " -x xtrace (set -x)\n"
110
+ exit 0;;
111
+ ?) printf "Usage: %s: [-hvx]\n" $0
112
+ exit 2;;
113
+ esac
114
+ done
115
+
116
+ set $LINECOOK_OPTIONS > /dev/null
117
+ <%= break_line " #{target_name} " %>
118
+
119
+ END_OF_TEMPLATE
120
+ # :startdoc:
121
+
122
+ # == Notes
123
+
124
+ # Use dev/null on set such that no options will not dump ENV into stdout.
125
+
126
+ def shebang(shell_path=DEFAULT_SHELL_PATH, env_path=DEFAULT_ENV_PATH)
127
+ @shell_path = shell_path
128
+ @env_path = env_path
129
+ eval(SHEBANG, binding, __FILE__, SHEBANG_LINE)
130
+ nil
131
+ end
132
+
133
+ def _shebang(*args, &block) # :nodoc:
134
+ capture { shebang(*args, &block) }
135
+ end
136
+ end
137
+ end
138
+ end
@@ -0,0 +1,11 @@
1
+ require 'linecook/shell/posix'
2
+ require 'linecook/shell/unix'
3
+ require 'linecook/shell/utils'
4
+
5
+ module Linecook
6
+ module Shell
7
+ include Posix
8
+ include Unix
9
+ include Utils
10
+ end
11
+ end
@@ -0,0 +1,111 @@
1
+ require 'ostruct'
2
+ require 'stringio'
3
+ require 'erb'
4
+
5
+ module Linecook
6
+ class Template
7
+ class << self
8
+ def build(template, locals, template_path=nil)
9
+ ERB.new(template).result(OpenStruct.new(locals).send(:binding))
10
+ end
11
+ end
12
+
13
+ attr_reader :erbout
14
+
15
+ def initialize
16
+ @erbout = StringIO.new
17
+ end
18
+
19
+ # Returns self (not the underlying erbout storage that actually receives
20
+ # the output lines). In the ERB context, this method directs erb outputs
21
+ # to Template#concat and into the redirect mechanism.
22
+ def _erbout
23
+ self
24
+ end
25
+
26
+ # Sets the underlying erbout storage to input.
27
+ def _erbout=(input)
28
+ end
29
+
30
+ # Concatenates the specified input to the underlying erbout storage.
31
+ def concat(input)
32
+ erbout << input
33
+ self
34
+ end
35
+
36
+ def capture(strip=true)
37
+ current, redirect = erbout, StringIO.new
38
+
39
+ begin
40
+ @erbout = redirect
41
+ yield
42
+ ensure
43
+ @erbout = current
44
+ end
45
+
46
+ str = redirect.string
47
+ str.strip! if strip
48
+ str
49
+ end
50
+
51
+ def indent(indent=' ', &block)
52
+ capture(&block).split("\n").each do |line|
53
+ concat "#{indent}#{line}\n"
54
+ end
55
+ self
56
+ end
57
+
58
+ def nest(*nestings)
59
+ options = nestings.last.kind_of?(Hash) ? nestings.pop : {}
60
+ indent = options[:indent] || " "
61
+ line_sep = options[:line_sep] || "\n"
62
+
63
+ content = capture { yield }
64
+ return content if nestings.empty?
65
+
66
+ depth = nestings.length
67
+ lines = [indent * depth + content.gsub(/#{line_sep}/, line_sep + indent * depth)]
68
+
69
+ nestings.reverse_each do |(start_line, end_line)|
70
+ depth -= 1
71
+ lines.unshift(indent * depth + start_line)
72
+ lines << (indent * depth + end_line)
73
+ end
74
+
75
+ concat lines.join(line_sep)
76
+ end
77
+
78
+ def rstrip(n=10)
79
+ yield if block_given?
80
+
81
+ pos = erbout.pos
82
+ n = pos if pos < n
83
+ start = pos - n
84
+
85
+ erbout.pos = start
86
+ tail = erbout.read(n).rstrip
87
+
88
+ erbout.pos = start
89
+ erbout.truncate start
90
+
91
+ tail.length == 0 && start > 0 ? rstrip(n * 2) : concat(tail)
92
+ end
93
+
94
+ def close
95
+ erbout.close unless closed?
96
+ self
97
+ end
98
+
99
+ def closed?
100
+ erbout.closed?
101
+ end
102
+
103
+ def result(&block)
104
+ instance_eval(&block) if block
105
+
106
+ erbout.flush
107
+ erbout.rewind
108
+ erbout.read
109
+ end
110
+ end
111
+ end
@@ -0,0 +1,77 @@
1
+ module Linecook
2
+ module Test
3
+ module FileTest
4
+ module ClassMethods
5
+ attr_accessor :class_dir
6
+
7
+ def self.extended(base)
8
+ # Infers the test directory from the calling file.
9
+ # 'some_class_test.rb' => 'some_class_test'
10
+ calling_file = caller[2].gsub(/:\d+(:in .*)?$/, "")
11
+ base.class_dir = calling_file.chomp(File.extname(calling_file))
12
+ end
13
+ end
14
+
15
+ module ModuleMethods
16
+ module_function
17
+
18
+ def included(base)
19
+ base.extend base.kind_of?(Class) ? ClassMethods : ModuleMethods
20
+ super
21
+ end
22
+ end
23
+
24
+ extend ModuleMethods
25
+
26
+ attr_reader :user_dir
27
+ attr_reader :method_dir
28
+
29
+ def setup
30
+ super
31
+ @user_dir = Dir.pwd
32
+ @method_dir = File.expand_path(method_name, self.class.class_dir)
33
+
34
+ cleanup method_dir
35
+ FileUtils.mkdir_p method_dir
36
+ Dir.chdir method_dir
37
+ end
38
+
39
+ def teardown
40
+ Dir.chdir user_dir
41
+
42
+ unless ENV["KEEP_OUTPUTS"] == "true"
43
+ cleanup class_dir
44
+ end
45
+
46
+ super
47
+ end
48
+
49
+ def cleanup(dir)
50
+ FileUtils.rm_r(dir) if File.exists?(dir)
51
+ end
52
+
53
+ def path(relative_path)
54
+ File.expand_path(relative_path, method_dir)
55
+ end
56
+
57
+ def prepare(relative_path)
58
+ target = path(relative_path)
59
+
60
+ target_dir = File.dirname(target)
61
+ FileUtils.mkdir_p(target_dir) unless File.exists?(target_dir)
62
+
63
+ target
64
+ end
65
+
66
+ def file(relative_path, &block)
67
+ target = prepare(relative_path)
68
+ block ? File.open(target, 'w', &block) : FileUtils.touch(target)
69
+ target
70
+ end
71
+
72
+ def class_dir
73
+ self.class.class_dir
74
+ end
75
+ end
76
+ end
77
+ end