lens 0.0.6 → 0.0.7

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 668eb65c8f06feb60eb761ab9dde6a5812c2ab59
4
- data.tar.gz: 936d0eb1621ca3ffdee9d42946017691836d87fb
3
+ metadata.gz: 9832b9e9d921f59bf9a46776e523c6d020f572a6
4
+ data.tar.gz: e328be7d6b3763760d75a510d128010b2e941a34
5
5
  SHA512:
6
- metadata.gz: 0aad6dbde95515dff8c8f064dce10858911d7ddb1a23ca6601e77040dff452fb76454337a2e48e107478ac634cb2a5b290c24b50979649c407d67849dda0fc69
7
- data.tar.gz: 14fc6b41e208e8604c2edc2ff0d8249b7869b72e1ed8fc3720898b176b324ecce0e3f053fd91186fb9878e044317e8c32074cdbcab6d87d005db17c1fa46fe01
6
+ metadata.gz: 5f8df18f130382daa10fa8e5abbe20f3d456ad8cb67fbfe2caffff1a8a9dbb597ae07a5f816ed447df840257e886c9ee846a16ac15d44841e02eb6a5f141cd54
7
+ data.tar.gz: 1accd8c65979e589951b7d4de4f1f1c3c5b9086dec8ef4d1c58173248e2388a9ce1cb94b4f7efb1006e3d0a18ab6e58d96e97ef2d43c9965f27a650198149d66
data/.gitignore CHANGED
@@ -1,2 +1,4 @@
1
1
  *.swp
2
2
  *.swo
3
+ coverage
4
+ /Gemfile.lock
data/.hound.yml ADDED
@@ -0,0 +1,2 @@
1
+ ruby:
2
+ config_file: .rubocop.yml
data/.rubocop.yml ADDED
@@ -0,0 +1,237 @@
1
+ AllCops:
2
+ Exclude:
3
+ - "vendor/**/*"
4
+ - "db/schema.rb"
5
+ - "spec/*_helper.rb"
6
+ - "bin/*"
7
+ UseCache: false
8
+ Style/CollectionMethods:
9
+ Description: Preferred collection methods.
10
+ StyleGuide: https://github.com/bbatsov/ruby-style-guide#map-find-select-reduce-size
11
+ Enabled: true
12
+ PreferredMethods:
13
+ collect: map
14
+ collect!: map!
15
+ find: detect
16
+ find_all: select
17
+ reduce: inject
18
+ Style/DotPosition:
19
+ Description: Checks the position of the dot in multi-line method calls.
20
+ StyleGuide: https://github.com/bbatsov/ruby-style-guide#consistent-multi-line-chains
21
+ Enabled: true
22
+ EnforcedStyle: leading
23
+ SupportedStyles:
24
+ - leading
25
+ - trailing
26
+ Style/FileName:
27
+ Description: Use snake_case for source file names.
28
+ StyleGuide: https://github.com/bbatsov/ruby-style-guide#snake-case-files
29
+ Enabled: false
30
+ Exclude: []
31
+ Style/GuardClause:
32
+ Description: Check for conditionals that can be replaced with guard clauses
33
+ StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-nested-conditionals
34
+ Enabled: true
35
+ MinBodyLength: 5
36
+ Style/IfUnlessModifier:
37
+ Description: Favor modifier if/unless usage when you have a single-line body.
38
+ StyleGuide: https://github.com/bbatsov/ruby-style-guide#if-as-a-modifier
39
+ Enabled: true
40
+ MaxLineLength: 100
41
+ Style/OptionHash:
42
+ Description: Don't use option hashes when you can use keyword arguments.
43
+ Enabled: false
44
+ Style/PercentLiteralDelimiters:
45
+ Description: Use `%`-literal delimiters consistently
46
+ StyleGuide: https://github.com/bbatsov/ruby-style-guide#percent-literal-braces
47
+ Enabled: false
48
+ PreferredDelimiters:
49
+ "%": "()"
50
+ "%i": "()"
51
+ "%q": "()"
52
+ "%Q": "()"
53
+ "%r": "{}"
54
+ "%s": "()"
55
+ "%w": "()"
56
+ "%W": "()"
57
+ "%x": "()"
58
+ Style/PredicateName:
59
+ Description: Check the names of predicate methods.
60
+ StyleGuide: https://github.com/bbatsov/ruby-style-guide#bool-methods-qmark
61
+ Enabled: true
62
+ NamePrefix:
63
+ - is_
64
+ - has_
65
+ - have_
66
+ NamePrefixBlacklist:
67
+ - is_
68
+ Exclude:
69
+ - spec/**/*
70
+ Style/RaiseArgs:
71
+ Description: Checks the arguments passed to raise/fail.
72
+ StyleGuide: https://github.com/bbatsov/ruby-style-guide#exception-class-messages
73
+ Enabled: false
74
+ EnforcedStyle: exploded
75
+ SupportedStyles:
76
+ - compact
77
+ - exploded
78
+ Style/SignalException:
79
+ Description: Checks for proper usage of fail and raise.
80
+ StyleGuide: https://github.com/bbatsov/ruby-style-guide#fail-method
81
+ Enabled: false
82
+ EnforcedStyle: semantic
83
+ SupportedStyles:
84
+ - only_raise
85
+ - only_fail
86
+ - semantic
87
+ Style/SingleLineBlockParams:
88
+ Description: Enforces the names of some block params.
89
+ StyleGuide: https://github.com/bbatsov/ruby-style-guide#reduce-blocks
90
+ Enabled: false
91
+ Methods:
92
+ - reduce:
93
+ - a
94
+ - e
95
+ - inject:
96
+ - a
97
+ - e
98
+ Style/SingleLineMethods:
99
+ Description: Avoid single-line methods.
100
+ StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-single-line-methods
101
+ Enabled: true
102
+ AllowIfMethodIsEmpty: true
103
+ Style/StringLiterals:
104
+ Description: Checks if uses of quotes match the configured preference.
105
+ StyleGuide: https://github.com/bbatsov/ruby-style-guide#consistent-string-literals
106
+ Enabled: true
107
+ EnforcedStyle: single_quotes
108
+ SupportedStyles:
109
+ - single_quotes
110
+ - double_quotes
111
+ Style/StringLiteralsInInterpolation:
112
+ Description: Checks if uses of quotes inside expressions in interpolated strings
113
+ match the configured preference.
114
+ Enabled: true
115
+ EnforcedStyle: single_quotes
116
+ SupportedStyles:
117
+ - single_quotes
118
+ - double_quotes
119
+ Style/TrailingComma:
120
+ Description: Checks for trailing comma in parameter lists and literals.
121
+ StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-trailing-array-commas
122
+ Enabled: true
123
+ EnforcedStyleForMultiline: no_comma
124
+ SupportedStyles:
125
+ - comma
126
+ - no_comma
127
+ Metrics/LineLength:
128
+ Max: 100
129
+ Metrics/AbcSize:
130
+ Description: A calculated magnitude based on number of assignments, branches, and
131
+ conditions.
132
+ Enabled: false
133
+ Max: 15
134
+ Metrics/ClassLength:
135
+ Description: Avoid classes longer than 100 lines of code.
136
+ Enabled: false
137
+ CountComments: false
138
+ Max: 100
139
+ Metrics/ModuleLength:
140
+ CountComments: false
141
+ Max: 100
142
+ Description: Avoid modules longer than 100 lines of code.
143
+ Enabled: false
144
+ Metrics/CyclomaticComplexity:
145
+ Description: A complexity metric that is strongly correlated to the number of test
146
+ cases needed to validate a method.
147
+ Enabled: false
148
+ Max: 6
149
+ Metrics/MethodLength:
150
+ Description: Avoid methods longer than 10 lines of code.
151
+ StyleGuide: https://github.com/bbatsov/ruby-style-guide#short-methods
152
+ Enabled: true
153
+ CountComments: false
154
+ Max: 20
155
+ Metrics/ParameterLists:
156
+ Description: Avoid parameter lists longer than three or four parameters.
157
+ StyleGuide: https://github.com/bbatsov/ruby-style-guide#too-many-params
158
+ Enabled: true
159
+ Max: 5
160
+ CountKeywordArgs: true
161
+ Metrics/PerceivedComplexity:
162
+ Description: A complexity metric geared towards measuring complexity for a human
163
+ reader.
164
+ Enabled: false
165
+ Max: 7
166
+ Lint/AssignmentInCondition:
167
+ Description: Don't use assignment in conditions.
168
+ StyleGuide: https://github.com/bbatsov/ruby-style-guide#safe-assignment-in-condition
169
+ Enabled: false
170
+ AllowSafeAssignment: true
171
+ Style/InlineComment:
172
+ Description: Avoid inline comments.
173
+ Enabled: false
174
+ Style/AccessorMethodName:
175
+ Description: Check the naming of accessor methods for get_/set_.
176
+ Enabled: true
177
+ Style/Alias:
178
+ Description: Use alias_method instead of alias.
179
+ StyleGuide: https://github.com/bbatsov/ruby-style-guide#alias-method
180
+ Enabled: false
181
+ Style/Documentation:
182
+ Description: Document classes and non-namespace modules.
183
+ Enabled: false
184
+ Style/DoubleNegation:
185
+ Description: Checks for uses of double negation (!!).
186
+ StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-bang-bang
187
+ Enabled: false
188
+ Style/EachWithObject:
189
+ Description: Prefer `each_with_object` over `inject` or `reduce`.
190
+ Enabled: false
191
+ Style/EmptyLiteral:
192
+ Description: Prefer literals to Array.new/Hash.new/String.new.
193
+ StyleGuide: https://github.com/bbatsov/ruby-style-guide#literal-array-hash
194
+ Enabled: false
195
+ Style/ModuleFunction:
196
+ Description: Checks for usage of `extend self` in modules.
197
+ StyleGuide: https://github.com/bbatsov/ruby-style-guide#module-function
198
+ Enabled: false
199
+ Style/OneLineConditional:
200
+ Description: Favor the ternary operator(?:) over if/then/else/end constructs.
201
+ StyleGuide: https://github.com/bbatsov/ruby-style-guide#ternary-operator
202
+ Enabled: false
203
+ Style/PerlBackrefs:
204
+ Description: Avoid Perl-style regex back references.
205
+ StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-perl-regexp-last-matchers
206
+ Enabled: false
207
+ Style/Send:
208
+ Description: Prefer `Object#__send__` or `Object#public_send` to `send`, as `send`
209
+ may overlap with existing methods.
210
+ StyleGuide: https://github.com/bbatsov/ruby-style-guide#prefer-public-send
211
+ Enabled: false
212
+ Style/SpecialGlobalVars:
213
+ Description: Avoid Perl-style global variables.
214
+ StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-cryptic-perlisms
215
+ Enabled: false
216
+ Style/VariableInterpolation:
217
+ Description: Don't interpolate global, instance and class variables directly in
218
+ strings.
219
+ StyleGuide: https://github.com/bbatsov/ruby-style-guide#curlies-interpolate
220
+ Enabled: false
221
+ Style/WhenThen:
222
+ Description: Use when x then ... for one-line cases.
223
+ StyleGuide: https://github.com/bbatsov/ruby-style-guide#one-line-cases
224
+ Enabled: false
225
+ Lint/EachWithObjectArgument:
226
+ Description: Check for immutable argument given to each_with_object.
227
+ Enabled: true
228
+ Lint/HandleExceptions:
229
+ Description: Don't suppress exception.
230
+ StyleGuide: https://github.com/bbatsov/ruby-style-guide#dont-hide-exceptions
231
+ Enabled: true
232
+ Lint/LiteralInCondition:
233
+ Description: Checks of literals used in conditions.
234
+ Enabled: false
235
+ Lint/LiteralInInterpolation:
236
+ Description: Checks for literals used in interpolation.
237
+ Enabled: false
data/.travis.yml ADDED
@@ -0,0 +1,22 @@
1
+ language: ruby
2
+ cache: bundler
3
+ sudo: false
4
+ rvm:
5
+ - 1.9.3
6
+ - 2.0.0
7
+ - 2.1.0
8
+ - 2.2.2
9
+ - 2.2.3
10
+ - ruby-head
11
+ - jruby-19mode
12
+ - jruby-head
13
+ - rbx-2
14
+ env:
15
+ global:
16
+ - JRUBY_OPTS="-J-Xmx1024M --debug"
17
+ matrix:
18
+ allow_failures:
19
+ - rvm: jruby-19mode
20
+ - rvm: jruby-head
21
+ - rvm: rbx-2
22
+ script: bundle exec rspec spec
data/Gemfile CHANGED
@@ -1,4 +1,8 @@
1
1
  source 'https://rubygems.org'
2
- gem 'rails', '>= 3.0'
3
2
 
4
3
  gemspec
4
+
5
+ group :test do
6
+ gem 'simplecov', require: false
7
+ gem 'codeclimate-test-reporter', require: false
8
+ end
data/README.md CHANGED
@@ -1,29 +1,36 @@
1
- # Lens
1
+ [![Build Status](https://travis-ci.org/lenshq/lens_client.svg?branch=master)](https://travis-ci.org/lenshq/lens_client)
2
+ [![Code Climate](https://codeclimate.com/github/lenshq/lens_client/badges/gpa.svg)](https://codeclimate.com/github/lenshq/lens_client)
3
+ [![Test Coverage](https://codeclimate.com/github/lenshq/lens_client/badges/coverage.svg)](https://codeclimate.com/github/lenshq/lens_client/coverage)
2
4
 
3
- TODO: Write a gem description
5
+ # Lens ruby gem
4
6
 
5
- ## Installation
7
+ ### Installation
6
8
 
7
- Add this line to your application's Gemfile:
9
+ Add to your Gemfile
8
10
 
9
- gem 'lens'
11
+ ```ruby
12
+ gem 'lens'
13
+ ```
10
14
 
11
- And then execute:
15
+ or, if you want bleeding edge,
12
16
 
13
- $ bundle
17
+ ```ruby
18
+ gem 'lens', github: 'lenshq/lens_client'
19
+ ```
14
20
 
15
- Or install it yourself as:
21
+ ### Configuration
16
22
 
17
- $ gem install lens
23
+ * Get your application key at [LensHQ site](http://lenshq.io)
24
+ * Create file `config/initializers/lens.rb`
25
+ * Configure Lens client (sample config below)
26
+ * Start sending your data to Lens =]
18
27
 
19
- ## Usage
20
-
21
- TODO: Write usage instructions here
22
-
23
- ## Contributing
24
-
25
- 1. Fork it ( https://github.com/[my-github-username]/lens/fork )
26
- 2. Create your feature branch (`git checkout -b my-new-feature`)
27
- 3. Commit your changes (`git commit -am 'Add some feature'`)
28
- 4. Push to the branch (`git push origin my-new-feature`)
29
- 5. Create a new Pull Request
28
+ ```ruby
29
+ # config/initializers/lens.rb
30
+ Lens.configure do |config|
31
+ config.app_key = 'super_secret_key'
32
+ config.protocol = 'http'
33
+ config.host = 'localhost'
34
+ config.port = 3001
35
+ end
36
+ ```
data/lens.gemspec CHANGED
@@ -4,22 +4,23 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
4
  require 'lens/version'
5
5
 
6
6
  Gem::Specification.new do |spec|
7
- spec.name = "lens"
7
+ spec.name = 'lens'
8
8
  spec.version = Lens::VERSION
9
- spec.authors = ["kgorin", "artygus", "zzet"]
10
- spec.email = ["me@kgor.in", "artygus@engineeram.net", "me@zzet.org"]
9
+ spec.authors = ['kgorin', 'artygus', 'zzet']
10
+ spec.email = ['me@kgor.in', 'artygus@engineeram.net', 'me@zzet.org']
11
11
  spec.summary = %q{Gem to send Rails request stats}
12
- spec.homepage = "https://github.com/lenshq/lens_client"
13
- spec.license = "MIT"
12
+ spec.homepage = 'https://github.com/lenshq/lens_client'
13
+ spec.license = 'MIT'
14
14
 
15
15
  spec.files = `git ls-files -z`.split("\x0")
16
16
  spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
17
17
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
18
- spec.require_paths = ["lib"]
18
+ spec.require_paths = ['lib']
19
19
 
20
- spec.add_development_dependency "bundler", "~> 1.6"
21
- spec.add_development_dependency "rake" , '~> 0'
22
- spec.add_development_dependency 'rspec' , '~> 3.2'
23
- spec.add_development_dependency "pry" , '~> 0.10'
24
- spec.add_development_dependency "pry-nav", '~> 0'
20
+ spec.add_development_dependency 'bundler', '~> 1.6'
21
+ spec.add_development_dependency 'rails', '>= 3.0'
22
+ spec.add_development_dependency 'rake', '~> 0'
23
+ spec.add_development_dependency 'rspec', '~> 3.2'
24
+ spec.add_development_dependency 'pry', '~> 0.10'
25
+ spec.add_development_dependency 'pry-nav', '~> 0'
25
26
  end
@@ -0,0 +1,52 @@
1
+ module Lens
2
+ if RUBY_ENGINE == 'ruby' && RUBY_VERSION >= '1.9'
3
+ require 'objspace'
4
+
5
+ class AllocationsData
6
+ def initialize
7
+ @enabled = false
8
+ @objects_count = 0
9
+ @objects_memory = 0
10
+ end
11
+
12
+ def enable
13
+ @enabled = true
14
+ end
15
+
16
+ def objects_count
17
+ @objects_count = calculate_objects_count if @enabled
18
+ @objects_count
19
+ end
20
+
21
+ def objects_memory
22
+ @objects_memory = calculate_objects_memory if @enabled
23
+ @objects_memory
24
+ end
25
+
26
+ private
27
+
28
+ def calculate_objects_count
29
+ obj_count = ObjectSpace.count_objects
30
+ obj_count[:TOTAL] - obj_count[:FREE]
31
+ end
32
+
33
+ def calculate_objects_memory
34
+ ObjectSpace.count_objects_size[:TOTAL]
35
+ end
36
+ end
37
+ end
38
+
39
+ unless defined?(::Lens::AllocationsData)
40
+ class AllocationsData
41
+ def enable; end
42
+
43
+ def objects_count
44
+ 0
45
+ end
46
+
47
+ def objects_memory
48
+ 0
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,13 @@
1
+ module Lens
2
+ module Compression
3
+ module Gzip
4
+ def self.compress(data)
5
+ GzipUtil.gzip(data)
6
+ end
7
+
8
+ def self.headers
9
+ { 'Content-Type' => 'gzip/json' }
10
+ end
11
+ end
12
+ end
13
+ end
@@ -14,16 +14,26 @@ module Lens
14
14
  @port || default_port
15
15
  end
16
16
 
17
+ def compressor
18
+ default_compressor
19
+ end
20
+
21
+ private
22
+
17
23
  def default_port
18
24
  80
19
25
  end
20
26
 
21
27
  def default_host
22
- 'lens.coub.com'
28
+ 'lenshq.io'
23
29
  end
24
30
 
25
31
  def default_protocol
26
- 'https'
32
+ 'http'
33
+ end
34
+
35
+ def default_compressor
36
+ Compression::Gzip
27
37
  end
28
38
  end
29
39
  end
data/lib/lens/core.rb CHANGED
@@ -1,11 +1,12 @@
1
- require "lens/version"
2
- require "lens/configuration"
3
- require "lens/sender"
4
- require "lens/event_formatter"
5
- require "lens/trace"
6
-
7
- module Lens
8
- if defined?(Rails)
9
- require "lens/railtie"
10
- end
11
- end
1
+ require 'lens/version'
2
+ require 'lens/exceptions'
3
+ require 'lens/configuration'
4
+ require 'lens/gzip_util'
5
+ require 'lens/compression'
6
+ require 'lens/sender'
7
+ require 'lens/event_formatter'
8
+ require 'lens/gc'
9
+ require 'lens/event'
10
+ require 'lens/trace'
11
+ require 'lens/worker'
12
+ require 'lens/allocations_data'
data/lib/lens/event.rb ADDED
@@ -0,0 +1,27 @@
1
+ module Lens
2
+ class Event
3
+ attr_reader :name, :time, :end, :transaction_id, :payload
4
+
5
+ def initialize(options = {})
6
+ raise ArgumentError unless all_params_present?(options)
7
+
8
+ @name = options[:name]
9
+ @time = options[:started]
10
+ @end = options[:finished]
11
+ @transaction_id = options[:transaction_id]
12
+ @payload = options[:payload]
13
+ end
14
+
15
+ def duration
16
+ 1000.0 * (self.end - time)
17
+ end
18
+
19
+ private
20
+
21
+ def all_params_present?(options)
22
+ [:name, :started, :finished, :transaction_id, :payload].all? do |key|
23
+ options.key? key
24
+ end
25
+ end
26
+ end
27
+ end
@@ -1,8 +1,10 @@
1
1
  module Lens
2
2
  class EventFormatter
3
- def initialize(event, records)
3
+ def initialize(event, records, gc_time = 0.0, allocations_data = Lens::AllocationsData.new)
4
4
  @event = event
5
- @records = records
5
+ @records = records.map{|payload| filter_payload(payload)}
6
+ @gc_time = gc_time
7
+ @allocations_data = allocations_data
6
8
  end
7
9
 
8
10
  def json_formatted
@@ -16,7 +18,7 @@ module Lens
16
18
  private
17
19
 
18
20
  def event_payload
19
- @event_payload ||= @event.payload
21
+ @event_payload ||= filter_payload(@event.payload)
20
22
  end
21
23
 
22
24
  def event_data
@@ -32,11 +34,38 @@ module Lens
32
34
  start: @event.time.to_f,
33
35
  end: @event.end.to_f,
34
36
  duration: @event.duration,
37
+ gc_time: @gc_time,
35
38
  event_name: @event.name,
36
39
  meta: {
37
- client_version: VERSION
38
- }
40
+ client_version: VERSION,
41
+ rails_version: ::Rails.version
42
+ },
43
+ objects_count: @allocations_data.objects_count,
44
+ objects_memory: @allocations_data.objects_memory
39
45
  }
40
46
  end
47
+
48
+ def filter_payload(payload)
49
+ payload.inject({}) do |res, h|
50
+ k, v = h
51
+
52
+ if v.is_a?(Hash)
53
+ res[k] = filter_payload(v)
54
+ elsif v.is_a?(File)
55
+ res[k] = {type: :file, path: v.path}
56
+ elsif v.is_a?(ActionDispatch::Http::UploadedFile)
57
+ res[k] = {
58
+ content_type: v.content_type,
59
+ headers: v.headers,
60
+ original_filename: v.original_filename,
61
+ tempfile_path: v.tempfile.path
62
+ }
63
+ else
64
+ res[k] = v
65
+ end
66
+
67
+ res
68
+ end
69
+ end
41
70
  end
42
71
  end
@@ -0,0 +1,3 @@
1
+ module Lens
2
+ class ConfigurationError < StandardError; end
3
+ end
data/lib/lens/gc.rb ADDED
@@ -0,0 +1,30 @@
1
+ module Lens
2
+ if defined?(::GC::Profiler)
3
+ class GC
4
+ def initialize
5
+ @total = 0
6
+ end
7
+
8
+ def enable
9
+ ::GC::Profiler.enable
10
+ end
11
+
12
+ def total_time
13
+ run = ::GC::Profiler.total_time
14
+
15
+ if run > 0
16
+ ::GC::Profiler.clear
17
+ end
18
+
19
+ @total += run
20
+ end
21
+ end
22
+ end
23
+
24
+ unless defined?(::Lens::GC)
25
+ class GC
26
+ def enable; end
27
+ def total_time; 0.0; end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,12 @@
1
+ require 'zlib'
2
+ require 'stringio'
3
+
4
+ module GzipUtil
5
+ def self.gzip(string)
6
+ io = StringIO.new('w')
7
+ writer = Zlib::GzipWriter.new(io)
8
+ writer.write(string)
9
+ writer.close
10
+ io.string
11
+ end
12
+ end