tee 0.0.1

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.
@@ -0,0 +1,5 @@
1
+ /.bundle/
2
+ /.yardoc/
3
+ /Gemfile.lock
4
+ /doc/
5
+ /pkg/
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format documentation
@@ -0,0 +1,5 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.2
4
+ - 1.9.3
5
+ - jruby-19mode
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source :rubygems
2
+
3
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 Masaki Takeuchi
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,127 @@
1
+ tee
2
+ ===
3
+
4
+ A class like `tee` command for Ruby.
5
+
6
+ [![Build Status](https://secure.travis-ci.org/m4i/tee.png)](http://travis-ci.org/m4i/tee)
7
+
8
+
9
+ Installation
10
+ ------------
11
+
12
+ Add this line to your application's Gemfile:
13
+
14
+ gem 'tee'
15
+
16
+ And then execute:
17
+
18
+ $ bundle
19
+
20
+ Or install it yourself as:
21
+
22
+ $ gem install tee
23
+
24
+
25
+ Examples
26
+ --------
27
+
28
+ ### Basic usage
29
+
30
+ ```ruby
31
+ Tee.open('a.txt') do |tee|
32
+ tee.puts 'foo'
33
+ end
34
+ ```
35
+
36
+ ```
37
+ # a.txt
38
+ foo
39
+
40
+ # STDOUT
41
+ foo
42
+ ```
43
+
44
+ ### Multiple files
45
+
46
+ ```ruby
47
+ Tee.open('a.txt', 'b.txt') do |tee|
48
+ tee.puts 'bar'
49
+ end
50
+ ```
51
+
52
+ ```
53
+ # a.txt
54
+ bar
55
+
56
+ # b.txt
57
+ bar
58
+
59
+ # STDOUT
60
+ bar
61
+ ```
62
+
63
+ ### Appending mode
64
+
65
+ ```ruby
66
+ Tee.open('a.txt', mode: 'a') do |tee|
67
+ tee.puts 'baz'
68
+ end
69
+ ```
70
+
71
+ ```
72
+ # a.txt
73
+ bar
74
+ baz
75
+
76
+ # STDOUT
77
+ baz
78
+ ```
79
+
80
+ ### Disable STDOUT
81
+
82
+ ```ruby
83
+ Tee.open('a.txt', 'b.txt', stdout: nil) do |tee|
84
+ tee.puts 'qux'
85
+ end
86
+ ```
87
+
88
+ ```
89
+ # a.txt
90
+ qux
91
+
92
+ # b.txt
93
+ qux
94
+ ```
95
+
96
+ ### IO instances
97
+
98
+ ```ruby
99
+ require 'stringio'
100
+
101
+ stringio = StringIO.new
102
+
103
+ open('a.txt', 'w') do |file|
104
+ Tee.open(file, stringio) do |tee|
105
+ tee.puts 'quux'
106
+ end
107
+ file.puts 'corge'
108
+ end
109
+
110
+ puts stringio.string
111
+ ```
112
+
113
+ ```
114
+ # a.txt
115
+ quux
116
+ corge
117
+
118
+ # STDOUT
119
+ quux
120
+ quux
121
+ ```
122
+
123
+
124
+ Copyright
125
+ ---------
126
+
127
+ Copyright (c) 2012 Masaki Takeuchi. See LICENSE for details.
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env rake
2
+
3
+ require 'bundler/gem_tasks'
4
+ require 'rspec/core/rake_task'
5
+
6
+ RSpec::Core::RakeTask.new(:spec)
7
+
8
+ task :default => :spec
@@ -0,0 +1,115 @@
1
+ require 'tee/version'
2
+
3
+ # @attr stdout [IO]
4
+ class Tee
5
+ class << self
6
+ # @overload open(*ios, options = {})
7
+ # A synonym for Tee.new
8
+ # @param (see #initialize)
9
+ # @return [Tee]
10
+ #
11
+ # @overload open(*ios, options = {}, &block)
12
+ # It will be passed the Tee as an argument,
13
+ # and the Tee will automatically be closed when the block terminates.
14
+ # @param (see #initialize)
15
+ # @yieldparam tee [Tee]
16
+ # @return the value of the block
17
+ def open(*args, &block)
18
+ if block_given?
19
+ tee = new(*args)
20
+ begin
21
+ yield tee
22
+ ensure
23
+ tee.send(:close_ios_opened_by_tee)
24
+ end
25
+ else
26
+ new(*args)
27
+ end
28
+ end
29
+ end
30
+
31
+ attr_accessor :stdout
32
+
33
+ # @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
39
+ def initialize(*ios)
40
+ @options = { mode: 'w' }
41
+ @options.update(ios.pop) if ios.last.is_a?(Hash)
42
+
43
+ @stdout = @options.key?(:stdout) ? @options.delete(:stdout) : $stdout
44
+
45
+ @ios = []
46
+ add(*ios)
47
+ end
48
+
49
+ # Add IOs
50
+ #
51
+ # @param ios [Array<IO,String>]
52
+ # @return [self]
53
+ def add(*ios)
54
+ open_args = [@options[:mode]]
55
+ open_args << @options[:perm] if @options[:perm]
56
+
57
+ _ios = []
58
+ begin
59
+ ios.each do |io|
60
+ _ios << (
61
+ io.respond_to?(:write) ?
62
+ [io, false] :
63
+ [File.open(io, *open_args), true]
64
+ )
65
+ end
66
+ rescue => e
67
+ close_ios_opened_by_tee(_ios) rescue nil
68
+ raise e
69
+ end
70
+ @ios.concat(_ios)
71
+
72
+ self
73
+ end
74
+
75
+ # Closes all IOs
76
+ #
77
+ # @return [nil]
78
+ def close
79
+ @ios.each { |io,| io.close }
80
+ nil
81
+ end
82
+
83
+ # Returns self
84
+ #
85
+ # @return [self]
86
+ def to_io
87
+ self
88
+ end
89
+
90
+ %w(
91
+ <<
92
+ flush
93
+ print
94
+ printf
95
+ putc
96
+ puts
97
+ syswrite
98
+ write
99
+ write_nonblock
100
+ ).each do |method|
101
+ class_eval(<<-EOS, __FILE__, __LINE__ + 1)
102
+ def #{method}(*args)
103
+ @ios.each { |io,| io.send(:#{method}, *args) }
104
+ @stdout.send(:#{method}, *args) if @stdout
105
+ end
106
+ EOS
107
+ end
108
+
109
+ private
110
+
111
+ def close_ios_opened_by_tee(ios = @ios)
112
+ ios.each { |io, opened| io.close if opened }
113
+ nil
114
+ end
115
+ end
@@ -0,0 +1,3 @@
1
+ class Tee
2
+ VERSION = '0.0.1'
3
+ end
@@ -0,0 +1,237 @@
1
+ require 'tee'
2
+
3
+ require 'stringio'
4
+ require 'tempfile'
5
+
6
+ def temppath
7
+ Tempfile.open(File.basename(__FILE__)) do |file|
8
+ file.path.tap do
9
+ file.close!
10
+ end
11
+ end
12
+ end
13
+
14
+ def create_dummy_file
15
+ temppath.tap do |path|
16
+ File.open(path, 'w') do |file|
17
+ file.write('dummy')
18
+ end
19
+ end
20
+ end
21
+
22
+ describe Tee do
23
+ describe '.open' do
24
+ context 'with a non-existing file path' do
25
+ let(:path) { temppath }
26
+
27
+ it 'creates a file at the path' do
28
+ expect { Tee.open(path) {} }.to change { File.exist?(path) }.from(false).to(true)
29
+ end
30
+
31
+ after { File.delete(path) }
32
+ end
33
+
34
+ context 'with an existing file path' do
35
+ let(:path) { create_dummy_file }
36
+
37
+ context 'without mode' do
38
+ it 'overwrites an existing file' do
39
+ Tee.open(path) {}
40
+ File.read(path).should be_empty
41
+ end
42
+ end
43
+
44
+ context 'in appending mode' do
45
+ it 'does not overwrite an existing file' do
46
+ Tee.open(path, mode: 'a') {}
47
+ File.read(path).should == 'dummy'
48
+ end
49
+ end
50
+
51
+ after { File.delete(path) }
52
+ end
53
+ end
54
+
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 }
58
+
59
+ before :all do
60
+ $stdout = StringIO.new
61
+ Tee.open(path) do |tee|
62
+ tee.write('foo')
63
+ end
64
+ end
65
+
66
+ after :all do
67
+ $stdout = STDOUT
68
+ File.delete(path)
69
+ end
70
+
71
+ it 'writes `foo` in STDOUT' do
72
+ $stdout.string.should == 'foo'
73
+ end
74
+
75
+ it 'overwrites an existing file and writes `foo` in the file' do
76
+ File.read(path).should == 'foo'
77
+ end
78
+ end
79
+
80
+ context 'when tee is opened with an existing file path in appending mode' do
81
+ let(:path) { create_dummy_file }
82
+
83
+ before :all do
84
+ $stdout = StringIO.new
85
+ Tee.open(path, mode: 'a') do |tee|
86
+ tee.write('foo')
87
+ end
88
+ end
89
+
90
+ after :all do
91
+ $stdout = STDOUT
92
+ File.delete(path)
93
+ end
94
+
95
+ it 'writes `foo` in STDOUT' do
96
+ $stdout.string.should == 'foo'
97
+ end
98
+
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
103
+
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')
109
+ end
110
+ end
111
+
112
+ after :all do
113
+ $stdout = STDOUT
114
+ end
115
+
116
+ it 'writes `foo` in STDOUT' do
117
+ $stdout.string.should == 'foo'
118
+ end
119
+ end
120
+
121
+ context 'when tee is opened with two paths' do
122
+ let(:path1) { temppath }
123
+ let(:path2) { temppath }
124
+
125
+ before :all do
126
+ $stdout = StringIO.new
127
+ Tee.open(path1, path2) do |tee|
128
+ tee.write('foo')
129
+ end
130
+ end
131
+
132
+ after :all do
133
+ $stdout = STDOUT
134
+ File.delete(path1, path2)
135
+ end
136
+
137
+ it 'writes `foo` in STDOUT' do
138
+ $stdout.string.should == 'foo'
139
+ end
140
+
141
+ it 'writes `foo` in the first file' do
142
+ File.read(path1).should == 'foo'
143
+ end
144
+
145
+ it 'writes `foo` in the second file' do
146
+ File.read(path2).should == 'foo'
147
+ end
148
+ end
149
+
150
+ context 'when tee is opened with an option `{ stdout: nil }`' do
151
+ let(:path) { temppath }
152
+
153
+ before :all do
154
+ $stdout = StringIO.new
155
+ Tee.open(path, stdout: nil) do |tee|
156
+ tee.write('foo')
157
+ end
158
+ end
159
+
160
+ after :all do
161
+ $stdout = STDOUT
162
+ File.delete(path)
163
+ end
164
+
165
+ it 'writes nothing in STDOUT' do
166
+ $stdout.string.should be_empty
167
+ end
168
+
169
+ it 'writes `foo` in the file' do
170
+ File.read(path).should == 'foo'
171
+ end
172
+ end
173
+
174
+ context 'when tee is opened with IO instances' do
175
+ let(:path) { temppath }
176
+
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
186
+ end
187
+
188
+ after :all do
189
+ $stdout = STDOUT
190
+ File.delete(path)
191
+ end
192
+
193
+ it 'writes `foo` in STDOUT' do
194
+ $stdout.string.should == 'foo'
195
+ end
196
+
197
+ it 'writes `foo` to the File instance' do
198
+ File.read(path).should == 'foobar'
199
+ end
200
+
201
+ it 'writes `foo` to the StringIO instance' do
202
+ @stringio.string.should == 'foo'
203
+ end
204
+ end
205
+ end
206
+
207
+ describe '#add' do
208
+ let(:path1) { temppath }
209
+ let(:path2) { temppath }
210
+
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')
217
+ end
218
+ end
219
+
220
+ after :all do
221
+ $stdout = STDOUT
222
+ File.delete(path1, path2)
223
+ end
224
+
225
+ it 'writes `foobar` in STDOUT' do
226
+ $stdout.string.should == 'foobar'
227
+ end
228
+
229
+ it 'writes `foobar` in the first file' do
230
+ File.read(path1).should == 'foobar'
231
+ end
232
+
233
+ it 'writes `bar` in the second file' do
234
+ File.read(path2).should == 'bar'
235
+ end
236
+ end
237
+ end
@@ -0,0 +1,22 @@
1
+ require File.expand_path('../lib/tee/version', __FILE__)
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = 'tee'
5
+ s.version = Tee::VERSION
6
+ s.summary = %q{A class like `tee` command.}
7
+ s.description = s.summary
8
+
9
+ s.homepage = 'https://github.com/m4i/tee'
10
+ s.license = 'MIT'
11
+ s.author = 'Masaki Takeuchi'
12
+ s.email = 'm.ishihara@gmail.com'
13
+
14
+ s.files = `git ls-files`.split($\)
15
+ s.executables = s.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
16
+ s.test_files = s.files.grep(%r{^(test|spec|features)/})
17
+
18
+ s.required_ruby_version = '>= 1.9.2'
19
+
20
+ s.add_development_dependency 'rake', '~> 0.9.2.2'
21
+ s.add_development_dependency 'rspec', '~> 2.11.0'
22
+ end
metadata ADDED
@@ -0,0 +1,82 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: tee
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Masaki Takeuchi
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-08-05 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rake
16
+ requirement: &70197438246820 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: 0.9.2.2
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: *70197438246820
25
+ - !ruby/object:Gem::Dependency
26
+ name: rspec
27
+ requirement: &70197438246140 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ~>
31
+ - !ruby/object:Gem::Version
32
+ version: 2.11.0
33
+ type: :development
34
+ prerelease: false
35
+ version_requirements: *70197438246140
36
+ description: A class like `tee` command.
37
+ email: m.ishihara@gmail.com
38
+ executables: []
39
+ extensions: []
40
+ extra_rdoc_files: []
41
+ files:
42
+ - .gitignore
43
+ - .rspec
44
+ - .travis.yml
45
+ - Gemfile
46
+ - LICENSE
47
+ - README.md
48
+ - Rakefile
49
+ - lib/tee.rb
50
+ - lib/tee/version.rb
51
+ - spec/tee_spec.rb
52
+ - tee.gemspec
53
+ homepage: https://github.com/m4i/tee
54
+ licenses:
55
+ - MIT
56
+ post_install_message:
57
+ rdoc_options: []
58
+ require_paths:
59
+ - lib
60
+ required_ruby_version: !ruby/object:Gem::Requirement
61
+ none: false
62
+ requirements:
63
+ - - ! '>='
64
+ - !ruby/object:Gem::Version
65
+ version: 1.9.2
66
+ required_rubygems_version: !ruby/object:Gem::Requirement
67
+ none: false
68
+ requirements:
69
+ - - ! '>='
70
+ - !ruby/object:Gem::Version
71
+ version: '0'
72
+ segments:
73
+ - 0
74
+ hash: 936261864365435023
75
+ requirements: []
76
+ rubyforge_project:
77
+ rubygems_version: 1.8.11
78
+ signing_key:
79
+ specification_version: 3
80
+ summary: A class like `tee` command.
81
+ test_files:
82
+ - spec/tee_spec.rb