skylight 0.0.7 → 0.0.10

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. checksums.yaml +7 -0
  2. data/bin/skylight +5 -0
  3. data/lib/skylight.rb +2 -0
  4. data/lib/skylight/cli.rb +84 -0
  5. data/lib/skylight/config.rb +53 -6
  6. data/lib/skylight/instrumenter.rb +4 -1
  7. data/lib/skylight/json_proto.rb +0 -3
  8. data/lib/skylight/middleware.rb +1 -0
  9. data/lib/skylight/normalize.rb +30 -5
  10. data/lib/skylight/normalize/process_action.rb +1 -1
  11. data/lib/skylight/normalize/render_collection.rb +2 -6
  12. data/lib/skylight/normalize/render_partial.rb +2 -5
  13. data/lib/skylight/normalize/render_template.rb +2 -5
  14. data/lib/skylight/normalize/sql.rb +2 -4
  15. data/lib/skylight/railtie.rb +30 -33
  16. data/lib/skylight/sanity_checker.rb +73 -0
  17. data/lib/skylight/subscriber.rb +1 -15
  18. data/lib/skylight/trace.rb +30 -5
  19. data/lib/skylight/util/http.rb +97 -0
  20. data/lib/skylight/vendor/highline.rb +1012 -0
  21. data/lib/skylight/vendor/highline/color_scheme.rb +134 -0
  22. data/lib/skylight/vendor/highline/compatibility.rb +16 -0
  23. data/lib/skylight/vendor/highline/import.rb +41 -0
  24. data/lib/skylight/vendor/highline/menu.rb +398 -0
  25. data/lib/skylight/vendor/highline/question.rb +475 -0
  26. data/lib/skylight/vendor/highline/simulate.rb +48 -0
  27. data/lib/skylight/vendor/highline/string_extensions.rb +131 -0
  28. data/lib/skylight/vendor/highline/style.rb +181 -0
  29. data/lib/skylight/vendor/highline/system_extensions.rb +218 -0
  30. data/lib/skylight/vendor/thor.rb +473 -0
  31. data/lib/skylight/vendor/thor/actions.rb +318 -0
  32. data/lib/skylight/vendor/thor/actions/create_file.rb +105 -0
  33. data/lib/skylight/vendor/thor/actions/create_link.rb +60 -0
  34. data/lib/skylight/vendor/thor/actions/directory.rb +119 -0
  35. data/lib/skylight/vendor/thor/actions/empty_directory.rb +137 -0
  36. data/lib/skylight/vendor/thor/actions/file_manipulation.rb +314 -0
  37. data/lib/skylight/vendor/thor/actions/inject_into_file.rb +109 -0
  38. data/lib/skylight/vendor/thor/base.rb +652 -0
  39. data/lib/skylight/vendor/thor/command.rb +136 -0
  40. data/lib/skylight/vendor/thor/core_ext/hash_with_indifferent_access.rb +80 -0
  41. data/lib/skylight/vendor/thor/core_ext/io_binary_read.rb +12 -0
  42. data/lib/skylight/vendor/thor/core_ext/ordered_hash.rb +100 -0
  43. data/lib/skylight/vendor/thor/error.rb +28 -0
  44. data/lib/skylight/vendor/thor/group.rb +282 -0
  45. data/lib/skylight/vendor/thor/invocation.rb +172 -0
  46. data/lib/skylight/vendor/thor/parser.rb +4 -0
  47. data/lib/skylight/vendor/thor/parser/argument.rb +74 -0
  48. data/lib/skylight/vendor/thor/parser/arguments.rb +171 -0
  49. data/lib/skylight/vendor/thor/parser/option.rb +121 -0
  50. data/lib/skylight/vendor/thor/parser/options.rb +218 -0
  51. data/lib/skylight/vendor/thor/rake_compat.rb +72 -0
  52. data/lib/skylight/vendor/thor/runner.rb +322 -0
  53. data/lib/skylight/vendor/thor/shell.rb +88 -0
  54. data/lib/skylight/vendor/thor/shell/basic.rb +393 -0
  55. data/lib/skylight/vendor/thor/shell/color.rb +148 -0
  56. data/lib/skylight/vendor/thor/shell/html.rb +127 -0
  57. data/lib/skylight/vendor/thor/util.rb +270 -0
  58. data/lib/skylight/vendor/thor/version.rb +3 -0
  59. data/lib/skylight/version.rb +1 -1
  60. data/lib/skylight/worker.rb +3 -58
  61. metadata +56 -18
@@ -0,0 +1,119 @@
1
+ require 'thor/actions/empty_directory'
2
+
3
+ class Thor
4
+ module Actions
5
+ # Copies recursively the files from source directory to root directory.
6
+ # If any of the files finishes with .tt, it's considered to be a template
7
+ # and is placed in the destination without the extension .tt. If any
8
+ # empty directory is found, it's copied and all .empty_directory files are
9
+ # ignored. If any file name is wrapped within % signs, the text within
10
+ # the % signs will be executed as a method and replaced with the returned
11
+ # value. Let's suppose a doc directory with the following files:
12
+ #
13
+ # doc/
14
+ # components/.empty_directory
15
+ # README
16
+ # rdoc.rb.tt
17
+ # %app_name%.rb
18
+ #
19
+ # When invoked as:
20
+ #
21
+ # directory "doc"
22
+ #
23
+ # It will create a doc directory in the destination with the following
24
+ # files (assuming that the `app_name` method returns the value "blog"):
25
+ #
26
+ # doc/
27
+ # components/
28
+ # README
29
+ # rdoc.rb
30
+ # blog.rb
31
+ #
32
+ # <b>Encoded path note:</b> Since Thor internals use Object#respond_to? to check if it can
33
+ # expand %something%, this `something` should be a public method in the class calling
34
+ # #directory. If a method is private, Thor stack raises PrivateMethodEncodedError.
35
+ #
36
+ # ==== Parameters
37
+ # source<String>:: the relative path to the source root.
38
+ # destination<String>:: the relative path to the destination root.
39
+ # config<Hash>:: give :verbose => false to not log the status.
40
+ # If :recursive => false, does not look for paths recursively.
41
+ # If :mode => :preserve, preserve the file mode from the source.
42
+ # If :exclude_pattern => /regexp/, prevents copying files that match that regexp.
43
+ #
44
+ # ==== Examples
45
+ #
46
+ # directory "doc"
47
+ # directory "doc", "docs", :recursive => false
48
+ #
49
+ def directory(source, *args, &block)
50
+ config = args.last.is_a?(Hash) ? args.pop : {}
51
+ destination = args.first || source
52
+ action Directory.new(self, source, destination || source, config, &block)
53
+ end
54
+
55
+ class Directory < EmptyDirectory #:nodoc:
56
+ attr_reader :source
57
+
58
+ def initialize(base, source, destination=nil, config={}, &block)
59
+ @source = File.expand_path(base.find_in_source_paths(source.to_s))
60
+ @block = block
61
+ super(base, destination, { :recursive => true }.merge(config))
62
+ end
63
+
64
+ def invoke!
65
+ base.empty_directory given_destination, config
66
+ execute!
67
+ end
68
+
69
+ def revoke!
70
+ execute!
71
+ end
72
+
73
+ protected
74
+
75
+ def execute!
76
+ lookup = Util.escape_globs(source)
77
+ lookup = config[:recursive] ? File.join(lookup, '**') : lookup
78
+ lookup = file_level_lookup(lookup)
79
+
80
+ files(lookup).sort.each do |file_source|
81
+ next if File.directory?(file_source)
82
+ next if config[:exclude_pattern] && file_source.match(config[:exclude_pattern])
83
+ file_destination = File.join(given_destination, file_source.gsub(source, '.'))
84
+ file_destination.gsub!('/./', '/')
85
+
86
+ case file_source
87
+ when /\.empty_directory$/
88
+ dirname = File.dirname(file_destination).gsub(/\/\.$/, '')
89
+ next if dirname == given_destination
90
+ base.empty_directory(dirname, config)
91
+ when /\.tt$/
92
+ destination = base.template(file_source, file_destination[0..-4], config, &@block)
93
+ else
94
+ destination = base.copy_file(file_source, file_destination, config, &@block)
95
+ end
96
+ end
97
+ end
98
+
99
+ if RUBY_VERSION < '2.0'
100
+ def file_level_lookup(previous_lookup)
101
+ File.join(previous_lookup, '{*,.[a-z]*}')
102
+ end
103
+
104
+ def files(lookup)
105
+ Dir[lookup]
106
+ end
107
+ else
108
+ def file_level_lookup(previous_lookup)
109
+ File.join(previous_lookup, '*')
110
+ end
111
+
112
+ def files(lookup)
113
+ Dir.glob(lookup, File::FNM_DOTMATCH)
114
+ end
115
+ end
116
+
117
+ end
118
+ end
119
+ end
@@ -0,0 +1,137 @@
1
+ class Thor
2
+ module Actions
3
+
4
+ # Creates an empty directory.
5
+ #
6
+ # ==== Parameters
7
+ # destination<String>:: the relative path to the destination root.
8
+ # config<Hash>:: give :verbose => false to not log the status.
9
+ #
10
+ # ==== Examples
11
+ #
12
+ # empty_directory "doc"
13
+ #
14
+ def empty_directory(destination, config={})
15
+ action EmptyDirectory.new(self, destination, config)
16
+ end
17
+
18
+ # Class which holds create directory logic. This is the base class for
19
+ # other actions like create_file and directory.
20
+ #
21
+ # This implementation is based in Templater actions, created by Jonas Nicklas
22
+ # and Michael S. Klishin under MIT LICENSE.
23
+ #
24
+ class EmptyDirectory #:nodoc:
25
+ attr_reader :base, :destination, :given_destination, :relative_destination, :config
26
+
27
+ # Initializes given the source and destination.
28
+ #
29
+ # ==== Parameters
30
+ # base<Thor::Base>:: A Thor::Base instance
31
+ # source<String>:: Relative path to the source of this file
32
+ # destination<String>:: Relative path to the destination of this file
33
+ # config<Hash>:: give :verbose => false to not log the status.
34
+ #
35
+ def initialize(base, destination, config={})
36
+ @base, @config = base, { :verbose => true }.merge(config)
37
+ self.destination = destination
38
+ end
39
+
40
+ # Checks if the destination file already exists.
41
+ #
42
+ # ==== Returns
43
+ # Boolean:: true if the file exists, false otherwise.
44
+ #
45
+ def exists?
46
+ ::File.exists?(destination)
47
+ end
48
+
49
+ def invoke!
50
+ invoke_with_conflict_check do
51
+ ::FileUtils.mkdir_p(destination)
52
+ end
53
+ end
54
+
55
+ def revoke!
56
+ say_status :remove, :red
57
+ ::FileUtils.rm_rf(destination) if !pretend? && exists?
58
+ given_destination
59
+ end
60
+
61
+ protected
62
+
63
+ # Shortcut for pretend.
64
+ #
65
+ def pretend?
66
+ base.options[:pretend]
67
+ end
68
+
69
+ # Sets the absolute destination value from a relative destination value.
70
+ # It also stores the given and relative destination. Let's suppose our
71
+ # script is being executed on "dest", it sets the destination root to
72
+ # "dest". The destination, given_destination and relative_destination
73
+ # are related in the following way:
74
+ #
75
+ # inside "bar" do
76
+ # empty_directory "baz"
77
+ # end
78
+ #
79
+ # destination #=> dest/bar/baz
80
+ # relative_destination #=> bar/baz
81
+ # given_destination #=> baz
82
+ #
83
+ def destination=(destination)
84
+ if destination
85
+ @given_destination = convert_encoded_instructions(destination.to_s)
86
+ @destination = ::File.expand_path(@given_destination, base.destination_root)
87
+ @relative_destination = base.relative_to_original_destination_root(@destination)
88
+ end
89
+ end
90
+
91
+ # Filenames in the encoded form are converted. If you have a file:
92
+ #
93
+ # %file_name%.rb
94
+ #
95
+ # It calls #file_name from the base and replaces %-string with the
96
+ # return value (should be String) of #file_name:
97
+ #
98
+ # user.rb
99
+ #
100
+ # The method referenced can be either public or private.
101
+ #
102
+ def convert_encoded_instructions(filename)
103
+ filename.gsub(/%(.*?)%/) do |initial_string|
104
+ method = $1.strip
105
+ base.respond_to?(method, true) ? base.send(method) : initial_string
106
+ end
107
+ end
108
+
109
+ # Receives a hash of options and just execute the block if some
110
+ # conditions are met.
111
+ #
112
+ def invoke_with_conflict_check(&block)
113
+ if exists?
114
+ on_conflict_behavior(&block)
115
+ else
116
+ say_status :create, :green
117
+ block.call unless pretend?
118
+ end
119
+
120
+ destination
121
+ end
122
+
123
+ # What to do when the destination file already exists.
124
+ #
125
+ def on_conflict_behavior(&block)
126
+ say_status :exist, :blue
127
+ end
128
+
129
+ # Shortcut to say_status shell method.
130
+ #
131
+ def say_status(status, color)
132
+ base.shell.say_status status, relative_destination, color if config[:verbose]
133
+ end
134
+
135
+ end
136
+ end
137
+ end
@@ -0,0 +1,314 @@
1
+ require 'erb'
2
+ require 'open-uri'
3
+
4
+ class Thor
5
+ module Actions
6
+
7
+ # Copies the file from the relative source to the relative destination. If
8
+ # the destination is not given it's assumed to be equal to the source.
9
+ #
10
+ # ==== Parameters
11
+ # source<String>:: the relative path to the source root.
12
+ # destination<String>:: the relative path to the destination root.
13
+ # config<Hash>:: give :verbose => false to not log the status, and
14
+ # :mode => :preserve, to preserve the file mode from the source.
15
+
16
+ #
17
+ # ==== Examples
18
+ #
19
+ # copy_file "README", "doc/README"
20
+ #
21
+ # copy_file "doc/README"
22
+ #
23
+ def copy_file(source, *args, &block)
24
+ config = args.last.is_a?(Hash) ? args.pop : {}
25
+ destination = args.first || source
26
+ source = File.expand_path(find_in_source_paths(source.to_s))
27
+
28
+ create_file destination, nil, config do
29
+ content = File.binread(source)
30
+ content = block.call(content) if block
31
+ content
32
+ end
33
+ if config[:mode] == :preserve
34
+ mode = File.stat(source).mode
35
+ chmod(destination, mode, config)
36
+ end
37
+ end
38
+
39
+ # Links the file from the relative source to the relative destination. If
40
+ # the destination is not given it's assumed to be equal to the source.
41
+ #
42
+ # ==== Parameters
43
+ # source<String>:: the relative path to the source root.
44
+ # destination<String>:: the relative path to the destination root.
45
+ # config<Hash>:: give :verbose => false to not log the status.
46
+ #
47
+ # ==== Examples
48
+ #
49
+ # link_file "README", "doc/README"
50
+ #
51
+ # link_file "doc/README"
52
+ #
53
+ def link_file(source, *args, &block)
54
+ config = args.last.is_a?(Hash) ? args.pop : {}
55
+ destination = args.first || source
56
+ source = File.expand_path(find_in_source_paths(source.to_s))
57
+
58
+ create_link destination, source, config
59
+ end
60
+
61
+ # Gets the content at the given address and places it at the given relative
62
+ # destination. If a block is given instead of destination, the content of
63
+ # the url is yielded and used as location.
64
+ #
65
+ # ==== Parameters
66
+ # source<String>:: the address of the given content.
67
+ # destination<String>:: the relative path to the destination root.
68
+ # config<Hash>:: give :verbose => false to not log the status.
69
+ #
70
+ # ==== Examples
71
+ #
72
+ # get "http://gist.github.com/103208", "doc/README"
73
+ #
74
+ # get "http://gist.github.com/103208" do |content|
75
+ # content.split("\n").first
76
+ # end
77
+ #
78
+ def get(source, *args, &block)
79
+ config = args.last.is_a?(Hash) ? args.pop : {}
80
+ destination = args.first
81
+
82
+ source = File.expand_path(find_in_source_paths(source.to_s)) unless source =~ /^https?\:\/\//
83
+ render = open(source) {|input| input.binmode.read }
84
+
85
+ destination ||= if block_given?
86
+ block.arity == 1 ? block.call(render) : block.call
87
+ else
88
+ File.basename(source)
89
+ end
90
+
91
+ create_file destination, render, config
92
+ end
93
+
94
+ # Gets an ERB template at the relative source, executes it and makes a copy
95
+ # at the relative destination. If the destination is not given it's assumed
96
+ # to be equal to the source removing .tt from the filename.
97
+ #
98
+ # ==== Parameters
99
+ # source<String>:: the relative path to the source root.
100
+ # destination<String>:: the relative path to the destination root.
101
+ # config<Hash>:: give :verbose => false to not log the status.
102
+ #
103
+ # ==== Examples
104
+ #
105
+ # template "README", "doc/README"
106
+ #
107
+ # template "doc/README"
108
+ #
109
+ def template(source, *args, &block)
110
+ config = args.last.is_a?(Hash) ? args.pop : {}
111
+ destination = args.first || source.sub(/\.tt$/, '')
112
+
113
+ source = File.expand_path(find_in_source_paths(source.to_s))
114
+ context = instance_eval('binding')
115
+
116
+ create_file destination, nil, config do
117
+ content = ERB.new(::File.binread(source), nil, '-', '@output_buffer').result(context)
118
+ content = block.call(content) if block
119
+ content
120
+ end
121
+ end
122
+
123
+ # Changes the mode of the given file or directory.
124
+ #
125
+ # ==== Parameters
126
+ # mode<Integer>:: the file mode
127
+ # path<String>:: the name of the file to change mode
128
+ # config<Hash>:: give :verbose => false to not log the status.
129
+ #
130
+ # ==== Example
131
+ #
132
+ # chmod "script/server", 0755
133
+ #
134
+ def chmod(path, mode, config={})
135
+ return unless behavior == :invoke
136
+ path = File.expand_path(path, destination_root)
137
+ say_status :chmod, relative_to_original_destination_root(path), config.fetch(:verbose, true)
138
+ FileUtils.chmod_R(mode, path) unless options[:pretend]
139
+ end
140
+
141
+ # Prepend text to a file. Since it depends on insert_into_file, it's reversible.
142
+ #
143
+ # ==== Parameters
144
+ # path<String>:: path of the file to be changed
145
+ # data<String>:: the data to prepend to the file, can be also given as a block.
146
+ # config<Hash>:: give :verbose => false to not log the status.
147
+ #
148
+ # ==== Example
149
+ #
150
+ # prepend_to_file 'config/environments/test.rb', 'config.gem "rspec"'
151
+ #
152
+ # prepend_to_file 'config/environments/test.rb' do
153
+ # 'config.gem "rspec"'
154
+ # end
155
+ #
156
+ def prepend_to_file(path, *args, &block)
157
+ config = args.last.is_a?(Hash) ? args.pop : {}
158
+ config.merge!(:after => /\A/)
159
+ insert_into_file(path, *(args << config), &block)
160
+ end
161
+ alias_method :prepend_file, :prepend_to_file
162
+
163
+ # Append text to a file. Since it depends on insert_into_file, it's reversible.
164
+ #
165
+ # ==== Parameters
166
+ # path<String>:: path of the file to be changed
167
+ # data<String>:: the data to append to the file, can be also given as a block.
168
+ # config<Hash>:: give :verbose => false to not log the status.
169
+ #
170
+ # ==== Example
171
+ #
172
+ # append_to_file 'config/environments/test.rb', 'config.gem "rspec"'
173
+ #
174
+ # append_to_file 'config/environments/test.rb' do
175
+ # 'config.gem "rspec"'
176
+ # end
177
+ #
178
+ def append_to_file(path, *args, &block)
179
+ config = args.last.is_a?(Hash) ? args.pop : {}
180
+ config.merge!(:before => /\z/)
181
+ insert_into_file(path, *(args << config), &block)
182
+ end
183
+ alias_method :append_file, :append_to_file
184
+
185
+ # Injects text right after the class definition. Since it depends on
186
+ # insert_into_file, it's reversible.
187
+ #
188
+ # ==== Parameters
189
+ # path<String>:: path of the file to be changed
190
+ # klass<String|Class>:: the class to be manipulated
191
+ # data<String>:: the data to append to the class, can be also given as a block.
192
+ # config<Hash>:: give :verbose => false to not log the status.
193
+ #
194
+ # ==== Examples
195
+ #
196
+ # inject_into_class "app/controllers/application_controller.rb", ApplicationController, " filter_parameter :password\n"
197
+ #
198
+ # inject_into_class "app/controllers/application_controller.rb", ApplicationController do
199
+ # " filter_parameter :password\n"
200
+ # end
201
+ #
202
+ def inject_into_class(path, klass, *args, &block)
203
+ config = args.last.is_a?(Hash) ? args.pop : {}
204
+ config.merge!(:after => /class #{klass}\n|class #{klass} .*\n/)
205
+ insert_into_file(path, *(args << config), &block)
206
+ end
207
+
208
+ # Run a regular expression replacement on a file.
209
+ #
210
+ # ==== Parameters
211
+ # path<String>:: path of the file to be changed
212
+ # flag<Regexp|String>:: the regexp or string to be replaced
213
+ # replacement<String>:: the replacement, can be also given as a block
214
+ # config<Hash>:: give :verbose => false to not log the status.
215
+ #
216
+ # ==== Example
217
+ #
218
+ # gsub_file 'app/controllers/application_controller.rb', /#\s*(filter_parameter_logging :password)/, '\1'
219
+ #
220
+ # gsub_file 'README', /rake/, :green do |match|
221
+ # match << " no more. Use thor!"
222
+ # end
223
+ #
224
+ def gsub_file(path, flag, *args, &block)
225
+ return unless behavior == :invoke
226
+ config = args.last.is_a?(Hash) ? args.pop : {}
227
+
228
+ path = File.expand_path(path, destination_root)
229
+ say_status :gsub, relative_to_original_destination_root(path), config.fetch(:verbose, true)
230
+
231
+ unless options[:pretend]
232
+ content = File.binread(path)
233
+ content.gsub!(flag, *args, &block)
234
+ File.open(path, 'wb') { |file| file.write(content) }
235
+ end
236
+ end
237
+
238
+ # Uncomment all lines matching a given regex. It will leave the space
239
+ # which existed before the comment hash in tact but will remove any spacing
240
+ # between the comment hash and the beginning of the line.
241
+ #
242
+ # ==== Parameters
243
+ # path<String>:: path of the file to be changed
244
+ # flag<Regexp|String>:: the regexp or string used to decide which lines to uncomment
245
+ # config<Hash>:: give :verbose => false to not log the status.
246
+ #
247
+ # ==== Example
248
+ #
249
+ # uncomment_lines 'config/initializers/session_store.rb', /active_record/
250
+ #
251
+ def uncomment_lines(path, flag, *args)
252
+ flag = flag.respond_to?(:source) ? flag.source : flag
253
+
254
+ gsub_file(path, /^(\s*)#[[:blank:]]*(.*#{flag})/, '\1\2', *args)
255
+ end
256
+
257
+ # Comment all lines matching a given regex. It will leave the space
258
+ # which existed before the beginning of the line in tact and will insert
259
+ # a single space after the comment hash.
260
+ #
261
+ # ==== Parameters
262
+ # path<String>:: path of the file to be changed
263
+ # flag<Regexp|String>:: the regexp or string used to decide which lines to comment
264
+ # config<Hash>:: give :verbose => false to not log the status.
265
+ #
266
+ # ==== Example
267
+ #
268
+ # comment_lines 'config/initializers/session_store.rb', /cookie_store/
269
+ #
270
+ def comment_lines(path, flag, *args)
271
+ flag = flag.respond_to?(:source) ? flag.source : flag
272
+
273
+ gsub_file(path, /^(\s*)([^#|\n]*#{flag})/, '\1# \2', *args)
274
+ end
275
+
276
+ # Removes a file at the given location.
277
+ #
278
+ # ==== Parameters
279
+ # path<String>:: path of the file to be changed
280
+ # config<Hash>:: give :verbose => false to not log the status.
281
+ #
282
+ # ==== Example
283
+ #
284
+ # remove_file 'README'
285
+ # remove_file 'app/controllers/application_controller.rb'
286
+ #
287
+ def remove_file(path, config={})
288
+ return unless behavior == :invoke
289
+ path = File.expand_path(path, destination_root)
290
+
291
+ say_status :remove, relative_to_original_destination_root(path), config.fetch(:verbose, true)
292
+ ::FileUtils.rm_rf(path) if !options[:pretend] && File.exists?(path)
293
+ end
294
+ alias :remove_dir :remove_file
295
+
296
+ private
297
+ attr_accessor :output_buffer
298
+ def concat(string)
299
+ @output_buffer.concat(string)
300
+ end
301
+
302
+ def capture(*args, &block)
303
+ with_output_buffer { block.call(*args) }
304
+ end
305
+
306
+ def with_output_buffer(buf = '') #:nodoc:
307
+ self.output_buffer, old_buffer = buf, output_buffer
308
+ yield
309
+ output_buffer
310
+ ensure
311
+ self.output_buffer = old_buffer
312
+ end
313
+ end
314
+ end