xcode-result-bundle-processor 1.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.
- checksums.yaml +7 -0
- data/.codeclimate.yml +23 -0
- data/.csslintrc +34 -0
- data/.eslintrc +253 -0
- data/.gitignore +6 -0
- data/.rspec +2 -0
- data/.rubocop.yml +1171 -0
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/.travis.yml +8 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +83 -0
- data/LICENSE +36 -0
- data/README.md +72 -0
- data/Rakefile +50 -0
- data/bin/xcode-result-bundle-processor +37 -0
- data/lib/xcoderesultbundleprocessor.rb +28 -0
- data/lib/xcoderesultbundleprocessor/activity_log_formatter.rb +20 -0
- data/lib/xcoderesultbundleprocessor/element_snapshot.rb +81 -0
- data/lib/xcoderesultbundleprocessor/indented_string_buffer.rb +33 -0
- data/lib/xcoderesultbundleprocessor/keyword_struct.rb +10 -0
- data/lib/xcoderesultbundleprocessor/log_deserializer.rb +26 -0
- data/lib/xcoderesultbundleprocessor/results_bundle.rb +45 -0
- data/lib/xcoderesultbundleprocessor/slf0/class_name_resolver.rb +26 -0
- data/lib/xcoderesultbundleprocessor/slf0/deserializer.rb +45 -0
- data/lib/xcoderesultbundleprocessor/slf0/model/dvtdocumentlocation.rb +16 -0
- data/lib/xcoderesultbundleprocessor/slf0/model/dvttextdocumentlocation.rb +23 -0
- data/lib/xcoderesultbundleprocessor/slf0/model/ideactivitylogmessage.rb +26 -0
- data/lib/xcoderesultbundleprocessor/slf0/model/ideactivitylogsection.rb +29 -0
- data/lib/xcoderesultbundleprocessor/slf0/model/ideactivitylogunittestsection.rb +24 -0
- data/lib/xcoderesultbundleprocessor/slf0/model/ideconsoleitem.rb +16 -0
- data/lib/xcoderesultbundleprocessor/slf0/tokenizer.rb +82 -0
- data/lib/xcoderesultbundleprocessor/snapshot_summary.rb +47 -0
- data/lib/xcoderesultbundleprocessor/test_summaries/html_report.rb +156 -0
- data/lib/xcoderesultbundleprocessor/test_summaries/test_summaries.rb +84 -0
- data/lib/xcoderesultbundleprocessor/test_summaries/text_report.rb +40 -0
- data/lib/xcoderesultbundleprocessor/version.rb +3 -0
- data/static/report.css +30 -0
- data/static/report.js +14 -0
- data/xcode-result-bundle-processor.gemspec +30 -0
- metadata +225 -0
data/.ruby-gemset
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
xcode-result-bundle-processor
|
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
ruby-2.2.3
|
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,83 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
xcode-result-bundle-processor (1.0.0)
|
5
|
+
CFPropertyList (~> 2.2)
|
6
|
+
awesome_print (~> 1.6)
|
7
|
+
markaby (~> 0.8)
|
8
|
+
methadone (~> 1.9.2)
|
9
|
+
|
10
|
+
GEM
|
11
|
+
remote: https://rubygems.org/
|
12
|
+
specs:
|
13
|
+
CFPropertyList (2.3.2)
|
14
|
+
aruba (0.10.2)
|
15
|
+
childprocess (~> 0.5.6)
|
16
|
+
contracts (~> 0.9)
|
17
|
+
cucumber (>= 1.3.19)
|
18
|
+
ffi (~> 1.9.10)
|
19
|
+
rspec-expectations (>= 2.99)
|
20
|
+
thor (~> 0.19)
|
21
|
+
awesome_print (1.6.1)
|
22
|
+
builder (3.2.2)
|
23
|
+
childprocess (0.5.7)
|
24
|
+
ffi (~> 1.0, >= 1.0.11)
|
25
|
+
codeclimate-test-reporter (0.4.8)
|
26
|
+
simplecov (>= 0.7.1, < 1.0.0)
|
27
|
+
contracts (0.12.0)
|
28
|
+
cucumber (2.1.0)
|
29
|
+
builder (>= 2.1.2)
|
30
|
+
cucumber-core (~> 1.3.0)
|
31
|
+
diff-lcs (>= 1.1.3)
|
32
|
+
gherkin3 (~> 3.1.0)
|
33
|
+
multi_json (>= 1.7.5, < 2.0)
|
34
|
+
multi_test (>= 0.1.2)
|
35
|
+
cucumber-core (1.3.0)
|
36
|
+
gherkin3 (~> 3.1.0)
|
37
|
+
diff-lcs (1.2.5)
|
38
|
+
docile (1.1.5)
|
39
|
+
ffi (1.9.10)
|
40
|
+
gherkin3 (3.1.2)
|
41
|
+
json (1.8.3)
|
42
|
+
markaby (0.8.0)
|
43
|
+
builder
|
44
|
+
methadone (1.9.2)
|
45
|
+
bundler
|
46
|
+
multi_json (1.11.2)
|
47
|
+
multi_test (0.1.2)
|
48
|
+
rake (10.4.2)
|
49
|
+
rdoc (4.2.0)
|
50
|
+
rspec (3.3.0)
|
51
|
+
rspec-core (~> 3.3.0)
|
52
|
+
rspec-expectations (~> 3.3.0)
|
53
|
+
rspec-mocks (~> 3.3.0)
|
54
|
+
rspec-core (3.3.2)
|
55
|
+
rspec-support (~> 3.3.0)
|
56
|
+
rspec-expectations (3.3.1)
|
57
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
58
|
+
rspec-support (~> 3.3.0)
|
59
|
+
rspec-mocks (3.3.2)
|
60
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
61
|
+
rspec-support (~> 3.3.0)
|
62
|
+
rspec-support (3.3.0)
|
63
|
+
simplecov (0.10.0)
|
64
|
+
docile (~> 1.1.0)
|
65
|
+
json (~> 1.8)
|
66
|
+
simplecov-html (~> 0.10.0)
|
67
|
+
simplecov-html (0.10.0)
|
68
|
+
thor (0.19.1)
|
69
|
+
|
70
|
+
PLATFORMS
|
71
|
+
ruby
|
72
|
+
|
73
|
+
DEPENDENCIES
|
74
|
+
aruba (~> 0.10)
|
75
|
+
bundler (~> 1.6)
|
76
|
+
codeclimate-test-reporter (~> 0.4)
|
77
|
+
rake (~> 10.4)
|
78
|
+
rdoc (~> 4.2)
|
79
|
+
rspec (~> 3.3)
|
80
|
+
xcode-result-bundle-processor!
|
81
|
+
|
82
|
+
BUNDLED WITH
|
83
|
+
1.10.6
|
data/LICENSE
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
# Copyfree Open Innovation License
|
2
|
+
|
3
|
+
This is version 0.4 of the Copyfree Open Innovation License.
|
4
|
+
|
5
|
+
## Terms and Conditions
|
6
|
+
|
7
|
+
Redistributions, modified or unmodified, in whole or in part, must retain
|
8
|
+
applicable copyright or other legal privilege notices, these conditions, and
|
9
|
+
the following license terms and disclaimer. Subject to these conditions, the
|
10
|
+
holder(s) of copyright or other legal privileges, author(s) or assembler(s),
|
11
|
+
and contributors of this work hereby grant to any person who obtains a copy of
|
12
|
+
this work in any form:
|
13
|
+
|
14
|
+
1. Permission to reproduce, modify, distribute, publish, sell, sublicense, use,
|
15
|
+
and/or otherwise deal in the licensed material without restriction.
|
16
|
+
|
17
|
+
2. A perpetual, worldwide, non-exclusive, royalty-free, irrevocable patent
|
18
|
+
license to reproduce, modify, distribute, publish, sell, use, and/or otherwise
|
19
|
+
deal in the licensed material without restriction, for any and all patents:
|
20
|
+
|
21
|
+
a. Held presently or in the future by each such holder of copyright or
|
22
|
+
other legal privilege, author or assembler, or contributor, necessarily
|
23
|
+
infringed by the contributions alone or by combination with the work, of
|
24
|
+
that privilege holder, author or assembler, or contributor.
|
25
|
+
|
26
|
+
b. Necessarily infringed by the work at the time that holder of copyright
|
27
|
+
or other privilege, author or assembler, or contributor made any
|
28
|
+
contribution to the work.
|
29
|
+
|
30
|
+
NO WARRANTY OF ANY KIND IS IMPLIED BY, OR SHOULD BE INFERRED FROM, THIS LICENSE
|
31
|
+
OR THE ACT OF DISTRIBUTION UNDER THE TERMS OF THIS LICENSE, INCLUDING BUT NOT
|
32
|
+
LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE,
|
33
|
+
AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS, ASSEMBLERS, OR HOLDERS OF
|
34
|
+
COPYRIGHT OR OTHER LEGAL PRIVILEGE BE LIABLE FOR ANY CLAIM, DAMAGES, OR OTHER
|
35
|
+
LIABILITY, WHETHER IN ACTION OF CONTRACT, TORT, OR OTHERWISE ARISING FROM, OUT
|
36
|
+
OF, OR IN CONNECTION WITH THE WORK OR THE USE OF OR OTHER DEALINGS IN THE WORK.
|
data/README.md
ADDED
@@ -0,0 +1,72 @@
|
|
1
|
+
[](https://travis-ci.org/americanexpress/xcode-result-bundle-processor)
|
2
|
+
[](https://codeclimate.com/github/americanexpress/xcode-result-bundle-processor)
|
3
|
+
[](https://codeclimate.com/github/americanexpress/xcode-result-bundle-processor/coverage)
|
4
|
+
[](https://codeclimate.com/github/americanexpress/xcode-result-bundle-processor)
|
5
|
+
|
6
|
+
# XcodeResultBundleProcessor
|
7
|
+
|
8
|
+
This tool converts into Xcode 7's machine-readable results bundle into a human-readable HTML report including
|
9
|
+
detailed activity logs and screenshots for UI tests.
|
10
|
+
|
11
|
+
## Installation
|
12
|
+
|
13
|
+
Add this line to your application's Gemfile:
|
14
|
+
|
15
|
+
gem 'xcode-result-bundle-processor'
|
16
|
+
|
17
|
+
And then execute:
|
18
|
+
|
19
|
+
$ bundle
|
20
|
+
|
21
|
+
Or install it yourself as:
|
22
|
+
|
23
|
+
$ gem install xcode-result-bundle-processor
|
24
|
+
|
25
|
+
## Usage
|
26
|
+
|
27
|
+
First, run your UI tests using an invocation like this:
|
28
|
+
|
29
|
+
xcodebuild -workspace MyWorkspace.xcworkspace \
|
30
|
+
-scheme 'My UI Tests' \
|
31
|
+
-sdk iphonesimulator \
|
32
|
+
-resultBundlePath "my_results_bundle" \
|
33
|
+
|
34
|
+
This will put a bunch of mystery files into `my_results_bundle`, which you can transform into un-mystery files
|
35
|
+
via
|
36
|
+
|
37
|
+
bundle exec xcode-result-bundle-processor --save-html-report report my_results_bundle
|
38
|
+
|
39
|
+
The report will appear in `report/index.html`
|
40
|
+
|
41
|
+
## Development
|
42
|
+
|
43
|
+
If you have RVM and change to the working directory, you'll be prompted to install the needed Ruby version if you
|
44
|
+
don't already have it. RVM will also handle creating a gemset.
|
45
|
+
|
46
|
+
To update the dependencies, run
|
47
|
+
|
48
|
+
bundle
|
49
|
+
|
50
|
+
To run tests, run
|
51
|
+
|
52
|
+
bundle exec rake
|
53
|
+
|
54
|
+
To build the gem, fun
|
55
|
+
|
56
|
+
bundle exec rake build
|
57
|
+
|
58
|
+
To execute the tool, run
|
59
|
+
|
60
|
+
bundle exec xcode-result-bundle-processor
|
61
|
+
|
62
|
+
## Contributing
|
63
|
+
|
64
|
+
Contributing is easy and fun! Some guidelines:
|
65
|
+
|
66
|
+
* If you're making a non-trivial change, consider reaching out to Manuel the Maintainer (mwudka@me.com) for an
|
67
|
+
architecture/implementation chat.
|
68
|
+
* Create an issue to track your work and describe your goals
|
69
|
+
* Branch from master and make your changes in the branch
|
70
|
+
* Add tests for any new functionality
|
71
|
+
* When you're happy with your changes and the builds pass, open a PR
|
72
|
+
* If no one has addressed your PR after a day or two, prod Manuel the Maintainer (mwudka@me.com)
|
data/Rakefile
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
def dump_load_path
|
2
|
+
puts $LOAD_PATH.join("\n")
|
3
|
+
found = nil
|
4
|
+
$LOAD_PATH.each do |path|
|
5
|
+
if File.exist?(File.join(path,"rspec"))
|
6
|
+
puts "Found rspec in #{path}"
|
7
|
+
if File.exist?(File.join(path,"rspec","core"))
|
8
|
+
puts "Found core"
|
9
|
+
if File.exist?(File.join(path,"rspec","core","rake_task"))
|
10
|
+
puts "Found rake_task"
|
11
|
+
found = path
|
12
|
+
else
|
13
|
+
puts "!! no rake_task"
|
14
|
+
end
|
15
|
+
else
|
16
|
+
puts "!!! no core"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
if found.nil?
|
21
|
+
puts "Didn't find rspec/core/rake_task anywhere"
|
22
|
+
else
|
23
|
+
puts "Found in #{path}"
|
24
|
+
end
|
25
|
+
end
|
26
|
+
require 'bundler'
|
27
|
+
require 'rake/clean'
|
28
|
+
|
29
|
+
require 'rspec/core/rake_task'
|
30
|
+
|
31
|
+
require 'cucumber'
|
32
|
+
require 'cucumber/rake/task'
|
33
|
+
gem 'rdoc' # we need the installed RDoc gem, not the system one
|
34
|
+
require 'rdoc/task'
|
35
|
+
|
36
|
+
include Rake::DSL
|
37
|
+
|
38
|
+
Bundler::GemHelper.install_tasks
|
39
|
+
|
40
|
+
RSpec::Core::RakeTask.new(:spec)
|
41
|
+
|
42
|
+
CUKE_RESULTS = 'results.html'
|
43
|
+
CLEAN << CUKE_RESULTS
|
44
|
+
Cucumber::Rake::Task.new(:features) do |t|
|
45
|
+
t.cucumber_opts = "features --format html -o #{CUKE_RESULTS} --format pretty --no-source -x"
|
46
|
+
t.fork = false
|
47
|
+
end
|
48
|
+
|
49
|
+
task :default => [:spec, :features]
|
50
|
+
|
@@ -0,0 +1,37 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'xcoderesultbundleprocessor'
|
4
|
+
|
5
|
+
class App
|
6
|
+
include Methadone::Main
|
7
|
+
include Methadone::CLILogging
|
8
|
+
|
9
|
+
main do |results_bundle_path|
|
10
|
+
change_logger(Methadone::CLILogger.new(STDERR, STDERR))
|
11
|
+
|
12
|
+
info "Preparing report for results bundle path at #{results_bundle_path}"
|
13
|
+
|
14
|
+
results_bundle = if File.directory?(results_bundle_path)
|
15
|
+
XcodeResultBundleProcessor::DirectoryResultsBundle.new(results_bundle_path)
|
16
|
+
else
|
17
|
+
XcodeResultBundleProcessor::TarballResultsBundle.new(results_bundle_path)
|
18
|
+
end
|
19
|
+
|
20
|
+
unless options['save-html-report'].nil?
|
21
|
+
XcodeResultBundleProcessor::TestSummaries::HTMLReport.new(results_bundle).save(options['save-html-report'])
|
22
|
+
end
|
23
|
+
|
24
|
+
debug XcodeResultBundleProcessor::TestSummaries::TextReport.format(results_bundle)
|
25
|
+
info 'Success!'
|
26
|
+
end
|
27
|
+
|
28
|
+
description 'Creates friendly report from Xcode results bundle'
|
29
|
+
arg :results_bundle_path
|
30
|
+
on('--save-html-report [report output dir]', 'Save a test result HTML report to given directory')
|
31
|
+
|
32
|
+
version XcodeResultBundleProcessor::VERSION
|
33
|
+
|
34
|
+
use_log_level_option :toggle_debug_on_signal => 'USR1'
|
35
|
+
|
36
|
+
go!
|
37
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'rubygems/package'
|
2
|
+
require 'cfpropertylist'
|
3
|
+
require 'awesome_print'
|
4
|
+
require 'methadone'
|
5
|
+
require 'markaby'
|
6
|
+
require 'optparse'
|
7
|
+
require 'ostruct'
|
8
|
+
|
9
|
+
require 'xcoderesultbundleprocessor/version'
|
10
|
+
require 'xcoderesultbundleprocessor/keyword_struct'
|
11
|
+
require 'xcoderesultbundleprocessor/indented_string_buffer'
|
12
|
+
require 'xcoderesultbundleprocessor/slf0/tokenizer'
|
13
|
+
require 'xcoderesultbundleprocessor/slf0/class_name_resolver'
|
14
|
+
require 'xcoderesultbundleprocessor/slf0/deserializer'
|
15
|
+
require 'xcoderesultbundleprocessor/slf0/model/dvtdocumentlocation'
|
16
|
+
require 'xcoderesultbundleprocessor/slf0/model/dvttextdocumentlocation'
|
17
|
+
require 'xcoderesultbundleprocessor/slf0/model/ideactivitylogmessage'
|
18
|
+
require 'xcoderesultbundleprocessor/slf0/model/ideactivitylogsection'
|
19
|
+
require 'xcoderesultbundleprocessor/slf0/model/ideactivitylogunittestsection'
|
20
|
+
require 'xcoderesultbundleprocessor/slf0/model/ideconsoleitem'
|
21
|
+
require 'xcoderesultbundleprocessor/activity_log_formatter'
|
22
|
+
require 'xcoderesultbundleprocessor/log_deserializer'
|
23
|
+
require 'xcoderesultbundleprocessor/test_summaries/test_summaries'
|
24
|
+
require 'xcoderesultbundleprocessor/test_summaries/html_report'
|
25
|
+
require 'xcoderesultbundleprocessor/test_summaries/text_report'
|
26
|
+
require 'xcoderesultbundleprocessor/results_bundle'
|
27
|
+
require 'xcoderesultbundleprocessor/element_snapshot'
|
28
|
+
require 'xcoderesultbundleprocessor/snapshot_summary'
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module XcodeResultBundleProcessor
|
2
|
+
module ActivityLogFormatter
|
3
|
+
include Methadone::CLILogging
|
4
|
+
|
5
|
+
def self.format(section)
|
6
|
+
debug "Formatting activity log section #{section.ai}"
|
7
|
+
|
8
|
+
return "\n" if section.nil?
|
9
|
+
|
10
|
+
buffer = IndentedStringBuffer.new
|
11
|
+
|
12
|
+
# The top-level section contains the complete log for all test runs, so grabbing that text is enough to
|
13
|
+
# get the full log
|
14
|
+
section = section.subsections.first
|
15
|
+
|
16
|
+
buffer << section.title << section.text.each_line
|
17
|
+
buffer.to_s
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
module XcodeResultBundleProcessor
|
2
|
+
class ElementSnapshot < OpenStruct
|
3
|
+
def initialize(plist)
|
4
|
+
super()
|
5
|
+
|
6
|
+
@objects = plist.value.value['$objects'].value
|
7
|
+
|
8
|
+
root_object = plist.value.value['$top'].value['root']
|
9
|
+
|
10
|
+
self._deserialize_object(root_object).each do |key, value|
|
11
|
+
self[key] = value
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def _deserialize_dictionary(cfdictionary)
|
16
|
+
if cfdictionary.value.key?('NS.rectval')
|
17
|
+
return self._deserialize_object(cfdictionary.value['NS.rectval'])
|
18
|
+
end
|
19
|
+
|
20
|
+
if cfdictionary.value.key?('$classname')
|
21
|
+
return cfdictionary.value['$classname'].value
|
22
|
+
end
|
23
|
+
|
24
|
+
ret = {}
|
25
|
+
if cfdictionary.value.key?('NS.keys') && cfdictionary.value.key?('NS.objects')
|
26
|
+
keys_and_values = cfdictionary.value['NS.keys'].value.zip(cfdictionary.value['NS.objects'].value)
|
27
|
+
keys_and_values.each do |key, value|
|
28
|
+
ret[self._deserialize_object(key).to_s.to_sym] = self._deserialize_object(value)
|
29
|
+
end
|
30
|
+
elsif cfdictionary.value.key?('NS.objects')
|
31
|
+
return cfdictionary.value['NS.objects'].value.map { |o| self._deserialize_object(o) }
|
32
|
+
else
|
33
|
+
cfdictionary.value.each do |key, value|
|
34
|
+
ret[key.to_sym] = self._deserialize_object(value)
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
ret
|
39
|
+
end
|
40
|
+
|
41
|
+
def _deserialize_object(object)
|
42
|
+
if object.is_a?(CFPropertyList::CFUid)
|
43
|
+
self._deserialize_object(@objects[object.value])
|
44
|
+
elsif object.is_a?(CFPropertyList::CFString) && object.value == '$null'
|
45
|
+
nil
|
46
|
+
elsif object.is_a?(CFPropertyList::CFDictionary)
|
47
|
+
self._deserialize_dictionary(object)
|
48
|
+
elsif object.is_a?(CFPropertyList::CFString) or object.is_a?(CFPropertyList::CFInteger) or object.is_a?(CFPropertyList::CFBoolean)
|
49
|
+
object.value
|
50
|
+
elsif object.is_a?(CFPropertyList::CFArray)
|
51
|
+
object.value.map { |o| self._deserialize_object(o) }
|
52
|
+
else
|
53
|
+
raise "Unsupported object type #{object.class}"
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def _deserialize_uid(uid)
|
58
|
+
object = @objects[uid.value]
|
59
|
+
|
60
|
+
if object.is_a?(CFPropertyList::CFUid)
|
61
|
+
_deserialize_uid(uid)
|
62
|
+
elsif object.is_a?(CFPropertyList::CFString) && object.value == '$null'
|
63
|
+
nil
|
64
|
+
elsif object.is_a?(CFPropertyList::CFDictionary)
|
65
|
+
hash = {}
|
66
|
+
|
67
|
+
object.value.each do |key, value|
|
68
|
+
if value.is_a?(CFPropertyList::CFUid)
|
69
|
+
hash[key] = self._deserialize_uid(value)
|
70
|
+
else
|
71
|
+
hash[key] = value.value
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
hash
|
76
|
+
else
|
77
|
+
object.value
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module XcodeResultBundleProcessor
|
2
|
+
class IndentedStringBuffer
|
3
|
+
def initialize(buffer=nil, indent_level=0)
|
4
|
+
@buffer = buffer || ''
|
5
|
+
@indent_spaces = 2
|
6
|
+
@indent_level = indent_level
|
7
|
+
end
|
8
|
+
|
9
|
+
def <<(arg)
|
10
|
+
Array(arg).each do |line|
|
11
|
+
@buffer << ' ' * (@indent_spaces * @indent_level) << line
|
12
|
+
@buffer << "\n" unless line.end_with?("\n")
|
13
|
+
@buffer
|
14
|
+
end
|
15
|
+
self
|
16
|
+
end
|
17
|
+
|
18
|
+
def add_newline
|
19
|
+
@buffer << "\n"
|
20
|
+
self
|
21
|
+
end
|
22
|
+
|
23
|
+
def indent
|
24
|
+
IndentedStringBuffer.new(@buffer, @indent_level + 1)
|
25
|
+
end
|
26
|
+
|
27
|
+
def to_s
|
28
|
+
return "\n" if @buffer.empty?
|
29
|
+
@buffer
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
end
|