rios 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile ADDED
@@ -0,0 +1,10 @@
1
+ source "http://rubygems.org"
2
+
3
+ gem "ruby-termios", ">= 0.9.6"
4
+
5
+ group :development do
6
+ gem "shoulda", ">= 0"
7
+ gem "bundler", "~> 1.0.0"
8
+ gem "jeweler", "~> 1.6.4"
9
+ gem "rcov", ">= 0"
10
+ end
data/LICENSE.txt ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2011 mooz
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,19 @@
1
+ = rios
2
+
3
+ Description goes here.
4
+
5
+ == Contributing to rios
6
+
7
+ * Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet
8
+ * Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it
9
+ * Fork the project
10
+ * Start a feature/bugfix branch
11
+ * Commit and push until you are happy with your contribution
12
+ * Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
13
+ * Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
14
+
15
+ == Copyright
16
+
17
+ Copyright (c) 2011 mooz. See LICENSE.txt for
18
+ further details.
19
+
data/Rakefile ADDED
@@ -0,0 +1,57 @@
1
+ # encoding: utf-8
2
+
3
+ require 'rubygems'
4
+ require 'bundler'
5
+ begin
6
+ Bundler.setup(:default, :development)
7
+ rescue Bundler::BundlerError => e
8
+ $stderr.puts e.message
9
+ $stderr.puts "Run `bundle install` to install missing gems"
10
+ exit e.status_code
11
+ end
12
+
13
+ require 'rake'
14
+
15
+ require 'jeweler'
16
+ Jeweler::Tasks.new do |gem|
17
+ gem.name = "rios"
18
+ gem.homepage = "https://github.com/mooz/rios"
19
+ gem.license = "MIT"
20
+ gem.email = "stillpedant@gmail.com"
21
+ gem.authors = ["mooz"]
22
+ gem.summary = %Q{A proxy framework for command line interfaces}
23
+ gem.description = %Q{Rios is a proxy framework which works as a proxy
24
+ for command line applications and allows developer to hook input/output
25
+ of the applications in a blazingly simple way.}
26
+ # native extension
27
+ gem.extensions = FileList['ext/**/extconf.rb']
28
+ end
29
+
30
+ Jeweler::RubygemsDotOrgTasks.new
31
+
32
+ require 'rake/testtask'
33
+ Rake::TestTask.new(:test) do |test|
34
+ test.libs << 'lib' << 'test'
35
+ test.pattern = 'test/**/test_*.rb'
36
+ test.verbose = true
37
+ end
38
+
39
+ require 'rcov/rcovtask'
40
+ Rcov::RcovTask.new do |test|
41
+ test.libs << 'test'
42
+ test.pattern = 'test/**/test_*.rb'
43
+ test.verbose = true
44
+ test.rcov_opts << '--exclude "gems/*"'
45
+ end
46
+
47
+ require 'rake/rdoctask'
48
+ Rake::RDocTask.new do |rdoc|
49
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
50
+
51
+ rdoc.rdoc_dir = 'rdoc'
52
+ rdoc.title = "rios #{version}"
53
+ rdoc.rdoc_files.include('README*')
54
+ rdoc.rdoc_files.include('lib/**/*.rb')
55
+ end
56
+
57
+ task :default => :test
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.0
data/bin/script.rb ADDED
@@ -0,0 +1,76 @@
1
+ #!/usr/bin/env ruby
2
+ # -*- coding: utf-8 -*-
3
+
4
+ require "pathname"
5
+
6
+ $LOAD_PATH.unshift(File.dirname(File.symlink?(__FILE__) ?
7
+ Pathname.new(__FILE__).realpath : __FILE__) + "/../lib")
8
+
9
+ require "rios/proxy"
10
+
11
+ module Color
12
+ def red(msg)
13
+ "\033[1;31m#{msg}\033[0m"
14
+ end
15
+ end
16
+
17
+ class Script
18
+ include Color
19
+
20
+ DEFAULT_SCRIPT = "typescript"
21
+
22
+ def initialize(path = DEFAULT_SCRIPT, options = {})
23
+ @proxy = Rios::Proxy.new
24
+ @file = open(path, options[:append] ? "a" : "w")
25
+ setup_listeners()
26
+ end
27
+
28
+ def start()
29
+ puts start_message
30
+ @proxy.listen
31
+ puts finish_message
32
+ end
33
+
34
+ private
35
+
36
+ def setup_listeners
37
+ @proxy.on_output { |s|
38
+ s.gsub!(/@/) { |match| red(match[0]) }
39
+ @file.syswrite(s)
40
+ s
41
+ }
42
+
43
+ @proxy.on_input { |s|
44
+ if s.index("$")
45
+ @proxy.input("a")
46
+ @proxy.output("You cannot input $. Haha!\n")
47
+ ""
48
+ else
49
+ nil
50
+ end
51
+ }
52
+
53
+ @proxy.on_finish {
54
+ @file.close
55
+ }
56
+ end
57
+
58
+ def decorate_message(message)
59
+ <<EOS
60
+ ============================================================
61
+ #{message}
62
+ ============================================================
63
+ EOS
64
+ end
65
+
66
+ def start_message()
67
+ decorate_message("Script started, file is #{@file.path}")
68
+ end
69
+
70
+ def finish_message()
71
+ decorate_message("Script finished, file is #{@file.path}")
72
+ end
73
+ end
74
+
75
+ script = Script.new()
76
+ script.start
@@ -0,0 +1,8 @@
1
+ require "mkmf"
2
+
3
+ extension_name = "util"
4
+
5
+ dir_config(extension_name)
6
+ if have_library("util")
7
+ create_makefile("rios/" + extension_name)
8
+ end
data/ext/rios/util.c ADDED
@@ -0,0 +1,28 @@
1
+ #include "util.h"
2
+ #include <pty.h>
3
+
4
+ VALUE Rios = Qnil;
5
+ VALUE Util = Qnil;
6
+
7
+ void Init_util() {
8
+ Rios = rb_define_module("Rios");
9
+ Util = rb_define_module_under(Rios, "Util");
10
+ /* rb_define_method(Util, "openpty", method_openpty, 1); */
11
+ rb_define_singleton_method(Util, "openpty", method_openpty, 1);
12
+ }
13
+
14
+ /* Rios::Util::openpty */
15
+ VALUE method_openpty(VALUE self, VALUE fd) {
16
+ int master;
17
+ int slave;
18
+ struct termios tt;
19
+ struct winsize win;
20
+ int fd_i = NUM2INT(fd);
21
+
22
+ tcgetattr(fd_i, &tt);
23
+ ioctl(fd_i, TIOCGWINSZ, &win);
24
+
25
+ openpty(&master, &slave, NULL, &tt, &win);
26
+
27
+ return rb_ary_new3(2, INT2NUM(master), INT2NUM(slave));
28
+ }
data/ext/rios/util.h ADDED
@@ -0,0 +1,5 @@
1
+ #include "ruby.h"
2
+
3
+ void Init_util();
4
+ VALUE method_openpty(VALUE self, VALUE fd);
5
+
data/lib/rios/proxy.rb ADDED
@@ -0,0 +1,137 @@
1
+ require "rios/util"
2
+ require "rios/terminal"
3
+
4
+ module Rios
5
+ class Proxy
6
+ BUFSIZE = 128
7
+ DEFAULT_COMMAND = ENV["SHELL"]
8
+
9
+ def initialize
10
+ @fd_master, @fd_slave = Util.openpty($stdin.fileno)
11
+ @input_filters = []
12
+ @output_filters = []
13
+ @on_finishes = []
14
+ end
15
+
16
+ ##
17
+ # register handler which will be called when user inputs characters
18
+ def on_input(&block)
19
+ @input_filters.push(block)
20
+ end
21
+
22
+ ##
23
+ # register handler which will be called when target program outputs characters
24
+ def on_output(&block)
25
+ @output_filters.push(block)
26
+ end
27
+
28
+ ##
29
+ # register handler which will be called when target program exits
30
+ def on_finish(&block)
31
+ @on_finishes.push(block)
32
+ end
33
+
34
+ ##
35
+ # emulate user input
36
+ def input(string)
37
+ terminal.master.syswrite(string)
38
+ end
39
+
40
+ ##
41
+ # output string to the stdout (usually terminal)
42
+ def output(string)
43
+ $stdout.syswrite(string)
44
+ end
45
+
46
+ ##
47
+ # begin proxy session
48
+ def listen(command = nil, &block)
49
+ @command = command || DEFAULT_COMMAND
50
+
51
+ in_raw_mode {
52
+ fork {
53
+ fork {
54
+ do_command(block)
55
+ }
56
+ do_output()
57
+ }
58
+ Signal.trap(:CHLD) { terminal.master.close() } # TODO: Is this OK?
59
+ do_input()
60
+ }
61
+ end
62
+
63
+ private
64
+
65
+ def apply_filters(s, filters)
66
+ filters.reduce(s) { |acc, filter|
67
+ res = filter.call(acc)
68
+ # when filter returens `nil', use previous value as output
69
+ res.nil? ? acc : res
70
+ }
71
+ end
72
+
73
+ def in_raw_mode
74
+ old_tt = Termios::tcgetattr($stdin)
75
+ raw_tt = old_tt.clone
76
+
77
+ begin
78
+ Terminal::set_raw_mode(raw_tt)
79
+ raw_tt.c_lflag &= ~Termios::ECHO
80
+ Termios.tcsetattr($stdin, Termios::TCSAFLUSH, raw_tt)
81
+ yield
82
+ ensure
83
+ Termios.tcsetattr($stdin, Termios::TCSAFLUSH, old_tt)
84
+ end
85
+ end
86
+
87
+ def create_terminal
88
+ Terminal.new(@fd_master, @fd_slave)
89
+ end
90
+
91
+ def terminal
92
+ @terminal || @terminal = create_terminal
93
+ end
94
+
95
+ def do_input
96
+ terminal.slave.close
97
+
98
+ begin
99
+ while s = $stdin.sysread(BUFSIZE) do
100
+ input(apply_filters(s, @input_filters))
101
+ end
102
+ rescue
103
+ end
104
+
105
+ @on_finishes.each { |block| block.call }
106
+ end
107
+
108
+ def do_output
109
+ $stdout.sync = true
110
+ $stdin.close
111
+ terminal.slave.close
112
+
113
+ begin
114
+ while s = terminal.master.sysread(BUFSIZE) do
115
+ output(apply_filters(s, @output_filters))
116
+ end
117
+ rescue
118
+ end
119
+
120
+ terminal.master.close
121
+ end
122
+
123
+ def do_command(block)
124
+ terminal.master.close
125
+ $stdin.reopen(terminal.slave)
126
+ $stdout.reopen(terminal.slave)
127
+ $stderr.reopen(terminal.slave)
128
+ terminal.slave.close
129
+
130
+ if block
131
+ block.call
132
+ else
133
+ exec(@command)
134
+ end
135
+ end
136
+ end
137
+ end
@@ -0,0 +1,38 @@
1
+ require "rubygems"
2
+ require "termios"
3
+
4
+ module Rios
5
+ class Terminal
6
+ def initialize(fd_master, fd_slave)
7
+ @fd_master, @fd_slave = fd_master, fd_slave
8
+ end
9
+
10
+ def synced(io)
11
+ io.sync = true unless io.closed?
12
+ io
13
+ end
14
+
15
+ def master
16
+ synced(@master ||= IO.open(@fd_master))
17
+ end
18
+
19
+ def slave
20
+ synced(@slave ||= IO.open(@fd_slave))
21
+ end
22
+
23
+ class << self
24
+ def set_raw_mode(termios)
25
+ termios.c_iflag &= ~(Termios::IGNBRK | Termios::BRKINT |
26
+ Termios::PARMRK | Termios::ISTRIP |
27
+ Termios::INLCR | Termios::IGNCR |
28
+ Termios::ICRNL | Termios::IXON)
29
+ termios.c_oflag &= ~Termios::OPOST
30
+ termios.c_lflag &= ~(Termios::ECHO | Termios::ECHONL |
31
+ Termios::ICANON | Termios::ISIG |
32
+ Termios::IEXTEN)
33
+ termios.c_cflag &= ~(Termios::CSIZE | Termios::PARENB)
34
+ termios.c_cflag |= Termios::CS8
35
+ end
36
+ end
37
+ end
38
+ end
data/lib/rios.rb ADDED
@@ -0,0 +1,5 @@
1
+ $:.unshift(File.dirname(__FILE__)) unless
2
+ $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
3
+
4
+ module Rios
5
+ end
data/rios.gemspec ADDED
@@ -0,0 +1,67 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{rios}
8
+ s.version = "0.1.0"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["mooz"]
12
+ s.date = %q{2011-07-15}
13
+ s.default_executable = %q{script.rb}
14
+ s.description = %q{Rios is a proxy framework which works as a proxy
15
+ for command line applications and allows developer to hook input/output
16
+ of the applications in a blazingly simple way.}
17
+ s.email = %q{stillpedant@gmail.com}
18
+ s.executables = ["script.rb"]
19
+ s.extensions = ["ext/rios/extconf.rb"]
20
+ s.extra_rdoc_files = [
21
+ "LICENSE.txt",
22
+ "README.rdoc"
23
+ ]
24
+ s.files = [
25
+ "Gemfile",
26
+ "Rakefile",
27
+ "VERSION",
28
+ "bin/script.rb",
29
+ "ext/rios/extconf.rb",
30
+ "ext/rios/util.c",
31
+ "ext/rios/util.h",
32
+ "lib/rios.rb",
33
+ "lib/rios/proxy.rb",
34
+ "lib/rios/terminal.rb",
35
+ "rios.gemspec"
36
+ ]
37
+ s.homepage = %q{https://github.com/mooz/rios}
38
+ s.licenses = ["MIT"]
39
+ s.require_paths = ["lib"]
40
+ s.rubygems_version = %q{1.6.2}
41
+ s.summary = %q{A proxy framework for command line interfaces}
42
+
43
+ if s.respond_to? :specification_version then
44
+ s.specification_version = 3
45
+
46
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
47
+ s.add_runtime_dependency(%q<ruby-termios>, [">= 0.9.6"])
48
+ s.add_development_dependency(%q<shoulda>, [">= 0"])
49
+ s.add_development_dependency(%q<bundler>, ["~> 1.0.0"])
50
+ s.add_development_dependency(%q<jeweler>, ["~> 1.6.4"])
51
+ s.add_development_dependency(%q<rcov>, [">= 0"])
52
+ else
53
+ s.add_dependency(%q<ruby-termios>, [">= 0.9.6"])
54
+ s.add_dependency(%q<shoulda>, [">= 0"])
55
+ s.add_dependency(%q<bundler>, ["~> 1.0.0"])
56
+ s.add_dependency(%q<jeweler>, ["~> 1.6.4"])
57
+ s.add_dependency(%q<rcov>, [">= 0"])
58
+ end
59
+ else
60
+ s.add_dependency(%q<ruby-termios>, [">= 0.9.6"])
61
+ s.add_dependency(%q<shoulda>, [">= 0"])
62
+ s.add_dependency(%q<bundler>, ["~> 1.0.0"])
63
+ s.add_dependency(%q<jeweler>, ["~> 1.6.4"])
64
+ s.add_dependency(%q<rcov>, [">= 0"])
65
+ end
66
+ end
67
+
metadata ADDED
@@ -0,0 +1,129 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rios
3
+ version: !ruby/object:Gem::Version
4
+ prerelease:
5
+ version: 0.1.0
6
+ platform: ruby
7
+ authors:
8
+ - mooz
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+
13
+ date: 2011-07-15 00:00:00 +09:00
14
+ default_executable: script.rb
15
+ dependencies:
16
+ - !ruby/object:Gem::Dependency
17
+ name: ruby-termios
18
+ requirement: &id001 !ruby/object:Gem::Requirement
19
+ none: false
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: 0.9.6
24
+ type: :runtime
25
+ prerelease: false
26
+ version_requirements: *id001
27
+ - !ruby/object:Gem::Dependency
28
+ name: shoulda
29
+ requirement: &id002 !ruby/object:Gem::Requirement
30
+ none: false
31
+ requirements:
32
+ - - ">="
33
+ - !ruby/object:Gem::Version
34
+ version: "0"
35
+ type: :development
36
+ prerelease: false
37
+ version_requirements: *id002
38
+ - !ruby/object:Gem::Dependency
39
+ name: bundler
40
+ requirement: &id003 !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ~>
44
+ - !ruby/object:Gem::Version
45
+ version: 1.0.0
46
+ type: :development
47
+ prerelease: false
48
+ version_requirements: *id003
49
+ - !ruby/object:Gem::Dependency
50
+ name: jeweler
51
+ requirement: &id004 !ruby/object:Gem::Requirement
52
+ none: false
53
+ requirements:
54
+ - - ~>
55
+ - !ruby/object:Gem::Version
56
+ version: 1.6.4
57
+ type: :development
58
+ prerelease: false
59
+ version_requirements: *id004
60
+ - !ruby/object:Gem::Dependency
61
+ name: rcov
62
+ requirement: &id005 !ruby/object:Gem::Requirement
63
+ none: false
64
+ requirements:
65
+ - - ">="
66
+ - !ruby/object:Gem::Version
67
+ version: "0"
68
+ type: :development
69
+ prerelease: false
70
+ version_requirements: *id005
71
+ description: |-
72
+ Rios is a proxy framework which works as a proxy
73
+ for command line applications and allows developer to hook input/output
74
+ of the applications in a blazingly simple way.
75
+ email: stillpedant@gmail.com
76
+ executables:
77
+ - script.rb
78
+ extensions:
79
+ - ext/rios/extconf.rb
80
+ extra_rdoc_files:
81
+ - LICENSE.txt
82
+ - README.rdoc
83
+ files:
84
+ - Gemfile
85
+ - Rakefile
86
+ - VERSION
87
+ - bin/script.rb
88
+ - ext/rios/extconf.rb
89
+ - ext/rios/util.c
90
+ - ext/rios/util.h
91
+ - lib/rios.rb
92
+ - lib/rios/proxy.rb
93
+ - lib/rios/terminal.rb
94
+ - rios.gemspec
95
+ - LICENSE.txt
96
+ - README.rdoc
97
+ has_rdoc: true
98
+ homepage: https://github.com/mooz/rios
99
+ licenses:
100
+ - MIT
101
+ post_install_message:
102
+ rdoc_options: []
103
+
104
+ require_paths:
105
+ - lib
106
+ required_ruby_version: !ruby/object:Gem::Requirement
107
+ none: false
108
+ requirements:
109
+ - - ">="
110
+ - !ruby/object:Gem::Version
111
+ hash: 998816175
112
+ segments:
113
+ - 0
114
+ version: "0"
115
+ required_rubygems_version: !ruby/object:Gem::Requirement
116
+ none: false
117
+ requirements:
118
+ - - ">="
119
+ - !ruby/object:Gem::Version
120
+ version: "0"
121
+ requirements: []
122
+
123
+ rubyforge_project:
124
+ rubygems_version: 1.6.2
125
+ signing_key:
126
+ specification_version: 3
127
+ summary: A proxy framework for command line interfaces
128
+ test_files: []
129
+