tee 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (6) hide show
  1. data/README.md +2 -2
  2. data/lib/tee.rb +121 -31
  3. data/spec/tee_spec.rb +218 -123
  4. data/tee.gemspec +2 -2
  5. metadata +9 -10
  6. data/lib/tee/version.rb +0 -3
data/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
  tee
2
2
  ===
3
3
 
4
- A class like `tee` command for Ruby.
4
+ A class like tee(1) for Ruby.
5
5
 
6
6
  [![Build Status](https://secure.travis-ci.org/m4i/tee.png)](http://travis-ci.org/m4i/tee)
7
7
 
@@ -103,7 +103,7 @@ stringio = StringIO.new
103
103
  open('a.txt', 'w') do |file|
104
104
  Tee.open(file, stringio) do |tee|
105
105
  tee.puts 'quux'
106
- end
106
+ end # `file` doesn't close because it wasn't opened by Tee.
107
107
  file.puts 'corge'
108
108
  end
109
109
 
data/lib/tee.rb CHANGED
@@ -1,17 +1,25 @@
1
- require 'tee/version'
2
-
3
- # @attr stdout [IO]
1
+ # A class like tee(1)
4
2
  class Tee
3
+ # @return [String]
4
+ VERSION = '0.0.2'
5
+
5
6
  class << self
7
+ # @macro new
8
+ # @param ios [Array<IO, String>]
9
+ # @param options [Hash]
10
+ # @option options [String, Fixnum] :mode ('w')
11
+ # @option options [Fixnum] :perm (0666)
12
+ # @option options [IO, nil] :stdout ($stdout)
13
+ #
6
14
  # @overload open(*ios, options = {})
7
15
  # A synonym for Tee.new
8
- # @param (see #initialize)
16
+ # @macro new
9
17
  # @return [Tee]
10
18
  #
11
19
  # @overload open(*ios, options = {}, &block)
12
20
  # It will be passed the Tee as an argument,
13
21
  # and the Tee will automatically be closed when the block terminates.
14
- # @param (see #initialize)
22
+ # @macro new
15
23
  # @yieldparam tee [Tee]
16
24
  # @return the value of the block
17
25
  def open(*args, &block)
@@ -20,7 +28,7 @@ class Tee
20
28
  begin
21
29
  yield tee
22
30
  ensure
23
- tee.send(:close_ios_opened_by_tee)
31
+ tee.send(:close_ios_opened_by_self)
24
32
  end
25
33
  else
26
34
  new(*args)
@@ -28,14 +36,12 @@ class Tee
28
36
  end
29
37
  end
30
38
 
39
+ # @param value [IO, nil] Sets the attribute stdout
40
+ # @return [IO, nil] Returns the value of attribute stdout
31
41
  attr_accessor :stdout
32
42
 
33
43
  # @overload initialize(*ios, options = {})
34
- # @param ios [Array<IO,String>]
35
- # @param options [Hash]
36
- # @option options [String, Fixnum] :mode
37
- # @option options [Fixnum] :perm
38
- # @option options [IO, nil] :stdout
44
+ # @macro new
39
45
  def initialize(*ios)
40
46
  @options = { mode: 'w' }
41
47
  @options.update(ios.pop) if ios.last.is_a?(Hash)
@@ -46,9 +52,9 @@ class Tee
46
52
  add(*ios)
47
53
  end
48
54
 
49
- # Add IOs
55
+ # Add ios
50
56
  #
51
- # @param ios [Array<IO,String>]
57
+ # @param ios [Array<IO, String>]
52
58
  # @return [self]
53
59
  def add(*ios)
54
60
  open_args = [@options[:mode]]
@@ -64,7 +70,7 @@ class Tee
64
70
  )
65
71
  end
66
72
  rescue => e
67
- close_ios_opened_by_tee(_ios) rescue nil
73
+ close_ios_opened_by_self(_ios) rescue nil
68
74
  raise e
69
75
  end
70
76
  @ios.concat(_ios)
@@ -72,14 +78,48 @@ class Tee
72
78
  self
73
79
  end
74
80
 
75
- # Closes all IOs
81
+ # Delegates #<< to ios
82
+ #
83
+ # @param obj [Object]
84
+ # @return [self]
85
+ def <<(obj)
86
+ each_ios_and_stdout { |io| io << obj }
87
+ self
88
+ end
89
+
90
+ # Closes all ios except stdout
76
91
  #
77
92
  # @return [nil]
78
93
  def close
79
- @ios.each { |io,| io.close }
94
+ each_ios(&:close)
80
95
  nil
81
96
  end
82
97
 
98
+ # Returns true if all ios except stdout is closed, false otherwise.
99
+ #
100
+ # @return [Boolean]
101
+ def closed?
102
+ each_ios.all?(&:closed?)
103
+ end
104
+
105
+ # Delegates #flush to ios
106
+ #
107
+ # @return [self]
108
+ def flush
109
+ each_ios_and_stdout(&:flush)
110
+ self
111
+ end
112
+
113
+ # Delegates #putc to ios
114
+ #
115
+ # @param char [Fixnum, String]
116
+ # @return [Fixnum]
117
+ # @return [String]
118
+ def putc(char)
119
+ each_ios_and_stdout { |io| io.putc(char) }
120
+ char
121
+ end
122
+
83
123
  # Returns self
84
124
  #
85
125
  # @return [self]
@@ -87,29 +127,79 @@ class Tee
87
127
  self
88
128
  end
89
129
 
90
- %w(
91
- <<
92
- flush
93
- print
94
- printf
95
- putc
96
- puts
97
- syswrite
98
- write
99
- write_nonblock
100
- ).each do |method|
130
+ # Delegates #tty? to stdout
131
+ #
132
+ # @return [Boolean]
133
+ def tty?
134
+ @stdout ? @stdout.tty? : false
135
+ end
136
+ alias isatty tty?
137
+
138
+ # @method print(obj, ...)
139
+ # Delegates #print to ios
140
+ # @param obj [Object]
141
+ # @return [nil]
142
+
143
+ # @method printf(format[, obj, ...])
144
+ # Delegates #printf to ios
145
+ # @param format [String]
146
+ # @param obj [Object]
147
+ # @return [nil]
148
+
149
+ # @method puts(obj, ...)
150
+ # Delegates #puts to ios
151
+ # @param obj [Object]
152
+ # @return [nil]
153
+ %w( print printf puts ).each do |method|
101
154
  class_eval(<<-EOS, __FILE__, __LINE__ + 1)
102
155
  def #{method}(*args)
103
- @ios.each { |io,| io.send(:#{method}, *args) }
104
- @stdout.send(:#{method}, *args) if @stdout
156
+ each_ios_and_stdout { |io| io.#{method}(*args) }
157
+ nil
158
+ end
159
+ EOS
160
+ end
161
+
162
+ # @method syswrite(string)
163
+ # Delegates #syswrite to ios
164
+ # @param string [String]
165
+ # @return [Array<Integer>]
166
+
167
+ # @method write(string)
168
+ # Delegates #write to ios
169
+ # @param string [String]
170
+ # @return [Array<Integer>]
171
+
172
+ # @method write_nonblock(string)
173
+ # Delegates #write_nonblock to ios
174
+ # @param string [String]
175
+ # @return [Array<Integer>]
176
+ %w( syswrite write write_nonblock ).each do |method|
177
+ class_eval(<<-EOS, __FILE__, __LINE__ + 1)
178
+ def #{method}(string)
179
+ each_ios_and_stdout.map { |io| io.#{method}(string) }
105
180
  end
106
181
  EOS
107
182
  end
108
183
 
109
184
  private
110
185
 
111
- def close_ios_opened_by_tee(ios = @ios)
112
- ios.each { |io, opened| io.close if opened }
186
+ def each_ios(&block)
187
+ return to_enum(:each_ios) unless block_given?
188
+ @ios.each do |io,|
189
+ yield io
190
+ end
191
+ self
192
+ end
193
+
194
+ def each_ios_and_stdout(&block)
195
+ return to_enum(:each_ios_and_stdout) unless block_given?
196
+ each_ios(&block)
197
+ yield @stdout if @stdout
198
+ self
199
+ end
200
+
201
+ def close_ios_opened_by_self(ios = @ios)
202
+ ios.each { |io, opened| io.close if opened && !io.closed? }
113
203
  nil
114
204
  end
115
205
  end
@@ -20,218 +20,313 @@ def create_dummy_file
20
20
  end
21
21
 
22
22
  describe Tee do
23
+ before { $stdout = StringIO.new }
24
+ after { $stdout = STDOUT }
25
+ let(:tee) { Tee.new }
26
+
23
27
  describe '.open' do
24
28
  context 'with a non-existing file path' do
25
29
  let(:path) { temppath }
30
+ after { File.delete(path) }
26
31
 
27
32
  it 'creates a file at the path' do
28
33
  expect { Tee.open(path) {} }.to change { File.exist?(path) }.from(false).to(true)
29
34
  end
30
-
31
- after { File.delete(path) }
32
35
  end
33
36
 
34
37
  context 'with an existing file path' do
35
38
  let(:path) { create_dummy_file }
39
+ after { File.delete(path) }
36
40
 
37
41
  context 'without mode' do
42
+ before { @tee = Tee.open(path) }
43
+ after { @tee.close }
44
+
38
45
  it 'overwrites an existing file' do
39
- Tee.open(path) {}
40
46
  File.read(path).should be_empty
41
47
  end
48
+
49
+ context 'when tee writes "foo"' do
50
+ before do
51
+ @tee.write('foo')
52
+ @tee.flush
53
+ end
54
+
55
+ it 'writes `foo` in STDOUT' do
56
+ $stdout.string.should == 'foo'
57
+ end
58
+
59
+ it 'writes `foo` in the file' do
60
+ File.read(path).should == 'foo'
61
+ end
62
+ end
42
63
  end
43
64
 
44
65
  context 'in appending mode' do
66
+ before do
67
+ @orignal_content = File.read(path)
68
+ @tee = Tee.open(path, mode: 'a')
69
+ end
70
+ after { @tee.close }
71
+
45
72
  it 'does not overwrite an existing file' do
46
- Tee.open(path, mode: 'a') {}
47
- File.read(path).should == 'dummy'
73
+ File.read(path).should == @orignal_content
48
74
  end
49
- end
50
75
 
51
- after { File.delete(path) }
52
- end
53
- end
76
+ context 'when tee writes "foo"' do
77
+ before do
78
+ @tee.write('foo')
79
+ @tee.flush
80
+ end
54
81
 
55
- describe '#write', 'with argument "foo"' do
56
- context 'when tee is opened with an existing file path' do
57
- let(:path) { create_dummy_file }
82
+ it 'writes `foo` in STDOUT' do
83
+ $stdout.string.should == 'foo'
84
+ end
58
85
 
59
- before :all do
60
- $stdout = StringIO.new
61
- Tee.open(path) do |tee|
62
- tee.write('foo')
86
+ it 'appends `foo` to the file' do
87
+ File.read(path).should == @orignal_content + 'foo'
88
+ end
63
89
  end
64
90
  end
91
+ end
65
92
 
66
- after :all do
67
- $stdout = STDOUT
68
- File.delete(path)
69
- end
93
+ context 'without arguments' do
94
+ context 'when tee writes "foo"' do
95
+ before { tee.write('foo') }
70
96
 
71
- it 'writes `foo` in STDOUT' do
72
- $stdout.string.should == 'foo'
97
+ it 'writes `foo` in STDOUT' do
98
+ $stdout.string.should == 'foo'
99
+ end
73
100
  end
101
+ end
74
102
 
75
- it 'overwrites an existing file and writes `foo` in the file' do
76
- File.read(path).should == 'foo'
103
+ context 'with two paths' do
104
+ let(:path1) { temppath }
105
+ let(:path2) { temppath }
106
+ before do
107
+ @tee = Tee.open(path1, path2)
108
+ end
109
+ after do
110
+ @tee.close
111
+ File.delete(path1, path2)
77
112
  end
78
- end
79
113
 
80
- context 'when tee is opened with an existing file path in appending mode' do
81
- let(:path) { create_dummy_file }
114
+ context 'when tee writes "foo"' do
115
+ before do
116
+ @tee.write('foo')
117
+ @tee.flush
118
+ end
82
119
 
83
- before :all do
84
- $stdout = StringIO.new
85
- Tee.open(path, mode: 'a') do |tee|
86
- tee.write('foo')
120
+ it 'writes `foo` in STDOUT' do
121
+ $stdout.string.should == 'foo'
122
+ end
123
+
124
+ it 'writes `foo` in the first file' do
125
+ File.read(path1).should == 'foo'
126
+ end
127
+
128
+ it 'writes `foo` in the second file' do
129
+ File.read(path2).should == 'foo'
87
130
  end
88
131
  end
132
+ end
89
133
 
90
- after :all do
91
- $stdout = STDOUT
134
+ context 'with an option `{ stdout: nil }`' do
135
+ let(:path) { temppath }
136
+ before do
137
+ @tee = Tee.open(path, stdout: nil)
138
+ end
139
+ after do
140
+ @tee.close
92
141
  File.delete(path)
93
142
  end
94
143
 
95
- it 'writes `foo` in STDOUT' do
96
- $stdout.string.should == 'foo'
97
- end
144
+ context 'when tee writes "foo"' do
145
+ before do
146
+ @tee.write('foo')
147
+ @tee.flush
148
+ end
98
149
 
99
- it 'does not overwrite an existing file and appends `foo` to the file' do
100
- File.read(path).should == 'dummyfoo'
101
- end
102
- end
150
+ it 'writes nothing in STDOUT' do
151
+ $stdout.string.should be_empty
152
+ end
103
153
 
104
- context 'when tee is opened without arguments' do
105
- before :all do
106
- $stdout = StringIO.new
107
- Tee.open do |tee|
108
- tee.write('foo')
154
+ it 'writes `foo` in the first file' do
155
+ File.read(path).should == 'foo'
109
156
  end
110
157
  end
158
+ end
111
159
 
112
- after :all do
113
- $stdout = STDOUT
160
+ context 'with File instance' do
161
+ let(:path) { temppath }
162
+ before do
163
+ @file = open(path, 'w')
164
+ @tee = Tee.open(@file)
114
165
  end
115
-
116
- it 'writes `foo` in STDOUT' do
117
- $stdout.string.should == 'foo'
166
+ after do
167
+ @file.close
168
+ File.delete(path)
118
169
  end
119
- end
120
170
 
121
- context 'when tee is opened with two paths' do
122
- let(:path1) { temppath }
123
- let(:path2) { temppath }
171
+ context 'when tee writes "foo"' do
172
+ before do
173
+ @tee.write('foo')
174
+ @tee.flush
175
+ end
124
176
 
125
- before :all do
126
- $stdout = StringIO.new
127
- Tee.open(path1, path2) do |tee|
128
- tee.write('foo')
177
+ it 'writes `foo` in STDOUT' do
178
+ $stdout.string.should == 'foo'
129
179
  end
130
- end
131
180
 
132
- after :all do
133
- $stdout = STDOUT
134
- File.delete(path1, path2)
181
+ it 'writes `foo` to the File instance' do
182
+ File.read(path).should == 'foo'
183
+ end
135
184
  end
185
+ end
136
186
 
137
- it 'writes `foo` in STDOUT' do
138
- $stdout.string.should == 'foo'
187
+ context 'with StringIO instance' do
188
+ before do
189
+ @stringio = StringIO.new
190
+ @tee = Tee.open(@stringio)
139
191
  end
140
192
 
141
- it 'writes `foo` in the first file' do
142
- File.read(path1).should == 'foo'
143
- end
193
+ context 'when tee writes "foo"' do
194
+ before do
195
+ @tee.write('foo')
196
+ @tee.flush
197
+ end
144
198
 
145
- it 'writes `foo` in the second file' do
146
- File.read(path2).should == 'foo'
199
+ it 'writes `foo` in STDOUT' do
200
+ $stdout.string.should == 'foo'
201
+ end
202
+
203
+ it 'writes `foo` to the StringIO instance' do
204
+ @stringio.string.should == 'foo'
205
+ end
147
206
  end
148
207
  end
208
+ end
149
209
 
150
- context 'when tee is opened with an option `{ stdout: nil }`' do
151
- let(:path) { temppath }
210
+ describe '#<<' do
211
+ it 'returns self' do
212
+ (tee << 'foo').should be tee
213
+ end
214
+ end
152
215
 
153
- before :all do
154
- $stdout = StringIO.new
155
- Tee.open(path, stdout: nil) do |tee|
216
+ describe '#add' do
217
+ it 'returns self' do
218
+ tee.add.should be tee
219
+ end
220
+
221
+ context 'when tee writes "foo", adds a file and writes "bar"' do
222
+ let(:file1) { StringIO.new }
223
+ let(:file2) { StringIO.new }
224
+
225
+ before do
226
+ Tee.open(file1) do |tee|
156
227
  tee.write('foo')
228
+ tee.add(file2)
229
+ tee.write('bar')
157
230
  end
158
231
  end
159
232
 
160
- after :all do
161
- $stdout = STDOUT
162
- File.delete(path)
233
+ it 'writes `foobar` in STDOUT' do
234
+ $stdout.string.should == 'foobar'
163
235
  end
164
236
 
165
- it 'writes nothing in STDOUT' do
166
- $stdout.string.should be_empty
237
+ it 'writes `foobar` in the first file' do
238
+ file1.string.should == 'foobar'
167
239
  end
168
240
 
169
- it 'writes `foo` in the file' do
170
- File.read(path).should == 'foo'
241
+ it 'writes `bar` in the second file' do
242
+ file2.string.should == 'bar'
171
243
  end
172
244
  end
245
+ end
173
246
 
174
- context 'when tee is opened with IO instances' do
175
- let(:path) { temppath }
247
+ describe '#close' do
248
+ it 'returns nil' do
249
+ tee.close.should be_nil
250
+ end
176
251
 
177
- before :all do
178
- $stdout = StringIO.new
179
- @stringio = StringIO.new
180
- open(path, 'w') do |file|
181
- Tee.open(file, @stringio) do |tee|
182
- tee.write('foo')
183
- end
184
- file.write('bar')
185
- end
252
+ it 'closes ios opened by self' do
253
+ path = temppath
254
+ Tee.open(path) do |tee|
255
+ expect { tee.close }.to change {
256
+ tee.instance_variable_get(:@ios)[0][0].closed?
257
+ }.from(false).to(true)
186
258
  end
259
+ File.delete(path)
260
+ end
187
261
 
188
- after :all do
189
- $stdout = STDOUT
190
- File.delete(path)
191
- end
262
+ it 'closes passed ios' do
263
+ file = open(temppath, 'w')
264
+ tee = Tee.open(file)
265
+ expect { tee.close }.to change { file.closed? }.from(false).to(true)
266
+ File.delete(file.path)
267
+ end
268
+ end
192
269
 
193
- it 'writes `foo` in STDOUT' do
194
- $stdout.string.should == 'foo'
195
- end
270
+ describe '#flush' do
271
+ it 'returns self' do
272
+ tee.flush.should be tee
273
+ end
274
+ end
196
275
 
197
- it 'writes `foo` to the File instance' do
198
- File.read(path).should == 'foobar'
199
- end
276
+ describe '#print' do
277
+ it 'returns nil' do
278
+ tee.print.should be_nil
279
+ end
280
+ end
200
281
 
201
- it 'writes `foo` to the StringIO instance' do
202
- @stringio.string.should == 'foo'
203
- end
282
+ describe '#printf' do
283
+ it 'returns nil' do
284
+ tee.printf('format').should be_nil
204
285
  end
205
286
  end
206
287
 
207
- describe '#add' do
208
- let(:path1) { temppath }
209
- let(:path2) { temppath }
288
+ describe '#putc' do
289
+ context 'with Fixnum argument' do
290
+ it 'returns argument' do
291
+ char = 0x20
292
+ tee.putc(char).should == char
293
+ end
294
+ end
210
295
 
211
- before :all do
212
- $stdout = StringIO.new
213
- Tee.open(path1) do |tee|
214
- tee.write('foo')
215
- tee.add(path2)
216
- tee.write('bar')
296
+ context 'with String argument' do
297
+ it 'returns argument' do
298
+ string = 'foo'
299
+ tee.putc(string).should == string
217
300
  end
218
301
  end
302
+ end
219
303
 
220
- after :all do
221
- $stdout = STDOUT
222
- File.delete(path1, path2)
304
+ describe '#puts' do
305
+ it 'returns nil' do
306
+ tee.puts.should be_nil
223
307
  end
308
+ end
224
309
 
225
- it 'writes `foobar` in STDOUT' do
226
- $stdout.string.should == 'foobar'
310
+ describe '#to_io' do
311
+ it 'returns self' do
312
+ tee.to_io.should be tee
227
313
  end
314
+ end
228
315
 
229
- it 'writes `foobar` in the first file' do
230
- File.read(path1).should == 'foobar'
316
+ %w( tty? isatty ).each do |method|
317
+ describe "##{method}" do
318
+ it 'returns $stdout.tty?' do
319
+ tee.send(method).should == $stdout.tty?
320
+ end
231
321
  end
322
+ end
232
323
 
233
- it 'writes `bar` in the second file' do
234
- File.read(path2).should == 'bar'
324
+ %w( syswrite write write_nonblock ).each do |method|
325
+ describe "##{method}" do
326
+ it 'returns Array of the number of bytes written' do
327
+ string = 'foo'
328
+ tee.send(method, string).should == [string.length]
329
+ end
235
330
  end
236
331
  end
237
332
  end
@@ -1,9 +1,9 @@
1
- require File.expand_path('../lib/tee/version', __FILE__)
1
+ require File.expand_path('../lib/tee', __FILE__)
2
2
 
3
3
  Gem::Specification.new do |s|
4
4
  s.name = 'tee'
5
5
  s.version = Tee::VERSION
6
- s.summary = %q{A class like `tee` command.}
6
+ s.summary = %q{A class like tee(1).}
7
7
  s.description = s.summary
8
8
 
9
9
  s.homepage = 'https://github.com/m4i/tee'
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tee
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-08-05 00:00:00.000000000 Z
12
+ date: 2012-08-06 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rake
16
- requirement: &70197438246820 !ruby/object:Gem::Requirement
16
+ requirement: &70332175132780 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ~>
@@ -21,10 +21,10 @@ dependencies:
21
21
  version: 0.9.2.2
22
22
  type: :development
23
23
  prerelease: false
24
- version_requirements: *70197438246820
24
+ version_requirements: *70332175132780
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: rspec
27
- requirement: &70197438246140 !ruby/object:Gem::Requirement
27
+ requirement: &70332175131820 !ruby/object:Gem::Requirement
28
28
  none: false
29
29
  requirements:
30
30
  - - ~>
@@ -32,8 +32,8 @@ dependencies:
32
32
  version: 2.11.0
33
33
  type: :development
34
34
  prerelease: false
35
- version_requirements: *70197438246140
36
- description: A class like `tee` command.
35
+ version_requirements: *70332175131820
36
+ description: A class like tee(1).
37
37
  email: m.ishihara@gmail.com
38
38
  executables: []
39
39
  extensions: []
@@ -47,7 +47,6 @@ files:
47
47
  - README.md
48
48
  - Rakefile
49
49
  - lib/tee.rb
50
- - lib/tee/version.rb
51
50
  - spec/tee_spec.rb
52
51
  - tee.gemspec
53
52
  homepage: https://github.com/m4i/tee
@@ -71,12 +70,12 @@ required_rubygems_version: !ruby/object:Gem::Requirement
71
70
  version: '0'
72
71
  segments:
73
72
  - 0
74
- hash: 936261864365435023
73
+ hash: 4540246555897654950
75
74
  requirements: []
76
75
  rubyforge_project:
77
76
  rubygems_version: 1.8.11
78
77
  signing_key:
79
78
  specification_version: 3
80
- summary: A class like `tee` command.
79
+ summary: A class like tee(1).
81
80
  test_files:
82
81
  - spec/tee_spec.rb
@@ -1,3 +0,0 @@
1
- class Tee
2
- VERSION = '0.0.1'
3
- end