terminus 0.1.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.
data/README.rdoc ADDED
@@ -0,0 +1,61 @@
1
+ = Terminus
2
+
3
+ * http://github.com/jcoglan/terminus
4
+
5
+ Terminus is a distributed JavaScript terminal. It lets you connect
6
+ web browsers you have open to a command-line console and run code
7
+ on all connected browsers at once. It's currently a toy, but it
8
+ might turn into a useful debugging and testing tool. We'll see.
9
+
10
+
11
+ == Installation
12
+
13
+ sudo gem install terminus
14
+
15
+
16
+ == Usage
17
+
18
+ In your terminal, run the +terminus+ command. You can optionally
19
+ specify a port for it to open up like this:
20
+
21
+ terminus --port 7004
22
+
23
+ You should see something like this:
24
+
25
+ Terminus running at http://0.0.0.0:7004
26
+ >>
27
+
28
+ This is the Terminus prompt - it's where you type in JavaScript
29
+ commands. Before you do that, though, visit http://localhost:7004
30
+ in your browser. From there you can grab a bookmarklet that lets
31
+ you connect any web page to the Terminus server. When you click
32
+ the bookmarklet, the current page will open a connection with
33
+ Terminus so that any commands you type into the shell are executed
34
+ on that page.
35
+
36
+
37
+ == License
38
+
39
+ (The MIT License)
40
+
41
+ Copyright (c) 2010 James Coglan
42
+
43
+ Permission is hereby granted, free of charge, to any person obtaining
44
+ a copy of this software and associated documentation files (the
45
+ 'Software'), to deal in the Software without restriction, including
46
+ without limitation the rights to use, copy, modify, merge, publish,
47
+ distribute, sublicense, and/or sell copies of the Software, and to
48
+ permit persons to whom the Software is furnished to do so, subject to
49
+ the following conditions:
50
+
51
+ The above copyright notice and this permission notice shall be
52
+ included in all copies or substantial portions of the Software.
53
+
54
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
55
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
56
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
57
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
58
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
59
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
60
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
61
+
data/bin/terminus ADDED
@@ -0,0 +1,33 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rubygems'
4
+ require 'readline'
5
+ require 'oyster'
6
+ require File.dirname(__FILE__) + '/../lib/terminus'
7
+
8
+ spec = Oyster.spec do
9
+ name "terminus -- controll web browsers from the command line"
10
+ synopsis "terminus [--port PORT]"
11
+
12
+ integer :port, :default => 7004
13
+ end
14
+
15
+ begin
16
+ options = spec.parse
17
+ app = Terminus.create(options)
18
+
19
+ app.run!
20
+ puts "Terminus running at http://0.0.0.0:#{options[:port]}"
21
+ puts "Press CTRL-C to exit"
22
+
23
+ trap("INT") { app.stop! ; exit }
24
+
25
+ loop {
26
+ script, result = Readline.readline('>> '), nil
27
+ Readline::HISTORY.push(script)
28
+ app.execute(script) { |r| result = r }
29
+ }
30
+
31
+ rescue Oyster::HelpRendered
32
+ end
33
+
@@ -0,0 +1 @@
1
+ this.JS=this.JS||{};JS.Package=function(a){var b=JS.Package.OrderedSet;JS.Package._3(this);this._0=a;this._1=new b();this._4=new b();this._9=new b();this._2={};this._5={}};(function(f){var l=f.OrderedSet=function(a,b){this._a=this.list=[];this._3={};if(!a)return;for(var c=0,d=a.length;c<d;c++)this.push(b?b(a[c]):a[c])};l.prototype.push=function(a){var b=(a.id!==undefined)?a.id:a,c=this._3;if(c.hasOwnProperty(b))return;c[b]=this._a.length;this._a.push(a)};f._b=this;if((this.document||{}).getElementsByTagName){var m=document.getElementsByTagName('script')[0];f._i=(m.readyState!==undefined)}var h=f.prototype;h.addDependency=function(a){this._4.push(a)};h.addSoftDependency=function(a){this._9.push(a)};h.addName=function(a){this._1.push(a);f.getFromCache(a).pkg=this};h.onload=function(a){this._c=a};h.on=function(a,b,c){if(this._5[a])return b.call(c);var d=this._2[a]=this._2[a]||[];d.push([b,c])};h.fire=function(a){if(this._5[a])return false;this._5[a]=true;var b=this._2[a];if(!b)return true;delete this._2[a];for(var c=0,d=b.length;c<d;c++)b[c][0].call(b[c][1]);return true};h.isLoaded=function(a){if(!a&&this._6!==undefined)return this._6;var b=this._1.list,c=b.length,d,e;while(c--){d=b[c];e=f.getObject(d);if(e!==undefined)continue;if(a)throw new Error('Expected package at '+this._0+' to define '+d);else return this._6=false}return this._6=true};h.load=function(){if(!this.fire('request'))return;var c=this._4.list.concat(this._9.list),d='load',e={};e[d]=this._4.list;f.when(e,function(){f.when({complete:c,load:[this]},function(){this.fire('complete')},this);var a=this,b=function(){if(a._c)a._c();a.isLoaded(true);a.fire('load')};if(this.isLoaded()){this.fire('download');return this.fire('load')}if(this._0===undefined)throw new Error('No load path found for '+this._1.list[0]);typeof this._0==='function'?this._0(b):f.Loader.loadFile(this._0,b);this.fire('download')},this)};h.toString=function(){return'Package:'+this._1.list.join(',')};f.when=function(b,c,d){var e=[],g,j,i;for(g in b){if(!b.hasOwnProperty(g))continue;j=new f.OrderedSet(b[g],function(a){return f.getByName(a)});i=j.list.length;while(i--)e.push([g,j.list[i]])}var k=i=e.length;if(k===0)return c&&c.call(d);while(i--){e[i][1].on(e[i][0],function(){k-=1;if(k===0&&c)c.call(d)});e[i][1].load()}};f._d=1;f._e={};f._f={};f._3=function(a){a.id=this._d;this._d+=1};f.getByPath=function(a){var b=a.toString();return this._e[b]=this._e[b]||new this(a)};f.getByName=function(a){if(typeof a!=='string')return a;var b=this.getFromCache(a);if(b.pkg)return b.pkg;var c=new this();c.addName(a);return c};f.getFromCache=function(a){return this._f[a]=this._f[a]||{}};f.getObject=function(a){var b=this.getFromCache(a);if(b.obj!==undefined)return b.obj;var c=this._b,d=a.split('.'),e;while(e=d.shift())c=c&&c[e];return this.getFromCache(a).obj=c}})(JS.Package);JS.Package.DomLoader={usable:function(){return!!JS.Package.getObject('window.document.getElementsByTagName')},__FILE__:function(){var a=document.getElementsByTagName('script');return a[a.length-1].src},loadFile:function(c,d){var e=this,g=document.createElement('script');g.type='text/javascript';g.src=c;g.onload=g.onreadystatechange=function(){var a=g.readyState,b=g.status;if(!a||a==='loaded'||a==='complete'||(a===4&&b===200)){d();g.onload=g.onreadystatechange=e._g;g=null}};if(window.console&&console.info)console.info('Loading '+c);document.getElementsByTagName('head')[0].appendChild(g)},_g:function(){}};JS.Package.ServerLoader={usable:function(){return typeof JS.Package.getObject('load')==='function'&&typeof JS.Package.getObject('version')==='function'},setup:function(){var b=this;load=(function(a){return function(){b._h=arguments[0];return a.apply(JS.Package._b,arguments)}})(load)},__FILE__:function(){return this._h},loadFile:function(a,b){load(a);b()}};(function(){var a=[JS.Package.DomLoader,JS.Package.ServerLoader],b=a.length,c,d;for(c=0;c<b;c++){d=a[c];if(d.usable()){JS.Package.Loader=d;if(d.setup)d.setup();break}}})();JS.Package.DSL={__FILE__:function(){return JS.Package.Loader.__FILE__()},pkg:function(a,b){var c=b?JS.Package.getByPath(b):JS.Package.getByName(a);c.addName(a);return new JS.Package.Description(c)},file:function(a){var b=JS.Package.getByPath(a);return new JS.Package.Description(b)},load:function(a,b){JS.Package.Loader.loadFile(a,b)}};JS.Package.Description=function(a){this._7=a};(function(e){e._8=function(a,b){var c=b.length,a=this._7[a],d;for(d=0;d<c;d++)a.call(this._7,b[d]);return this};e.provides=function(){return this._8('addName',arguments)};e.requires=function(){return this._8('addDependency',arguments)};e.uses=function(){return this._8('addSoftDependency',arguments)};e.setup=function(a){this._7.onload(a);return this}})(JS.Package.Description.prototype);JS.Package.DSL.loader=JS.Package.DSL.file;JS.Packages=function(a){a.call(JS.Package.DSL)};JS.require=function(){var a=[],b=0;while(typeof arguments[b]==='string'){a.push(arguments[b]);b+=1}JS.Package.when({complete:a},arguments[b],arguments[b+1])};require=JS.require;
@@ -0,0 +1,44 @@
1
+ body {
2
+ background: #222;
3
+ margin: 0;
4
+ padding: 40px;
5
+ }
6
+
7
+ .container {
8
+ background: #fff;
9
+ color: #444;
10
+ margin: 0 auto;
11
+ padding: 40px;
12
+ width: 400px;
13
+ font: 16px/1.5 Georgia, Times New Roman, serif;
14
+ }
15
+
16
+ .footer {
17
+ margin: 0 auto;
18
+ padding: 40px;
19
+ width: 400px;
20
+ color: #fff;
21
+ font: 13px Helvetica, Arial, sans-serif;
22
+ }
23
+
24
+ h1 {
25
+ font: bold 64px Helvetica, Arial, sans-serif;
26
+ letter-spacing: -0.03em;
27
+ margin: 0 0 32px;
28
+ color: #f00846;
29
+ }
30
+
31
+ a {
32
+ color: #f00846;
33
+ text-decoration: none;
34
+ }
35
+
36
+ a:hover {
37
+ background: #f00846;
38
+ color: #fff;
39
+ }
40
+
41
+ p {
42
+ margin: 0;
43
+ }
44
+
@@ -0,0 +1,15 @@
1
+ Terminus = {
2
+ connect: function(endpoint) {
3
+ if (this._client) return;
4
+
5
+ this._client = new Faye.Client(endpoint);
6
+ this._client.subscribe('/terminus/commands', function(message) {
7
+ this.execute(message.command);
8
+ }, this);
9
+ },
10
+
11
+ execute: function(command) {
12
+ eval(command);
13
+ }
14
+ };
15
+
data/lib/terminus.rb ADDED
@@ -0,0 +1,23 @@
1
+ require 'rubygems'
2
+ require 'rack'
3
+ require 'thin'
4
+ require 'eventmachine'
5
+ require 'faye'
6
+ require 'sinatra'
7
+ require 'packr'
8
+
9
+ %w[application server].each do |file|
10
+ require File.join(File.dirname(__FILE__), 'terminus', file)
11
+ end
12
+
13
+ Thin::Logging.silent = true
14
+
15
+ module Terminus
16
+ VERSION = '0.1.0'
17
+ FAYE_MOUNT = '/messaging'
18
+
19
+ def self.create(options = {})
20
+ Server.new(options)
21
+ end
22
+ end
23
+
@@ -0,0 +1,24 @@
1
+ module Terminus
2
+ class Application < Sinatra::Base
3
+
4
+ ROOT = File.expand_path(File.dirname(__FILE__) + '/../')
5
+
6
+ set :static, true
7
+ set :public, ROOT + '/public'
8
+ set :views, ROOT + '/views'
9
+
10
+ helpers do
11
+ def host
12
+ "http://#{ env['HTTP_HOST'] }"
13
+ end
14
+
15
+ def bookmarklet
16
+ Packr.pack(erb(:bookmarklet), :shrink_vars => true)
17
+ end
18
+ end
19
+
20
+ get('/') { erb :index }
21
+
22
+ end
23
+ end
24
+
@@ -0,0 +1,48 @@
1
+ module Terminus
2
+ class Server
3
+
4
+ def initialize(options = {})
5
+ @options = options
6
+ end
7
+
8
+ def execute(script, &callback)
9
+ messenger.publish('/terminus/commands', :command => script)
10
+ end
11
+
12
+ def running?
13
+ not @server.nil?
14
+ end
15
+
16
+ def run!
17
+ return if running?
18
+ handler = Rack::Handler.get('thin')
19
+ ensure_reactor_running!
20
+ handler.run(app, :Port => @options[:port]) { |s| @server = s }
21
+ end
22
+
23
+ def stop!
24
+ return unless running?
25
+ @server.stop!
26
+ @server = nil
27
+ end
28
+
29
+ private
30
+
31
+ def app
32
+ return @app if defined?(@app)
33
+ frontend = Application.new
34
+ @app = Faye::RackAdapter.new(frontend, :mount => FAYE_MOUNT, :timeout => 15)
35
+ end
36
+
37
+ def messenger
38
+ app.get_client
39
+ end
40
+
41
+ def ensure_reactor_running!
42
+ Thread.new { EM.run unless EM.reactor_running? }
43
+ while not EM.reactor_running?; end
44
+ end
45
+
46
+ end
47
+ end
48
+
@@ -0,0 +1,40 @@
1
+ (function() {
2
+ var faye = '<%= ::Terminus::FAYE_MOUNT %>',
3
+ host = '<%= host %>';
4
+
5
+ var withPackageManager = function(callback) {
6
+ if (window.JS && JS.Packages) return callback();
7
+
8
+ var script = document.createElement('script'),
9
+ head = document.getElementsByTagName('head')[0];
10
+
11
+ script.type = 'text/javascript';
12
+ script.src = host + '/js.packages.js';
13
+
14
+ script.onload = script.onreadystatechange = function() {
15
+ var state = script.readyState;
16
+ if (!state || state === 'loaded' || state === 'complete') {
17
+ script.onload = script.onreadystatechange = null;
18
+ head.removeChild(script);
19
+ callback();
20
+ }
21
+ };
22
+ head.appendChild(script);
23
+ };
24
+
25
+ withPackageManager(function() {
26
+ JS.Packages(function() {
27
+ this.file(host + faye + '/client.js')
28
+ .provides('Faye', 'Faye.Client');
29
+
30
+ this.file(host + '/terminus.js')
31
+ .requires('Faye.Client')
32
+ .provides('Terminus');
33
+ });
34
+
35
+ require('Terminus', function() {
36
+ Terminus.connect(host + faye);
37
+ });
38
+ });
39
+ })();
40
+
@@ -0,0 +1,29 @@
1
+ <!doctype html>
2
+
3
+ <html>
4
+ <head>
5
+ <meta http-equiv="content-type" content="text/html; charset=utf-8">
6
+ <link rel="stylesheet" type="text/css" href="/style.css">
7
+ <title>Terminus</title>
8
+ </head>
9
+ <body>
10
+
11
+ <div class="container">
12
+ <h1><a href="javascript:<%= bookmarklet %>">Terminus</a>.</h1>
13
+
14
+ <p>Terminus is a distributed JavaScript console. You can find out
15
+ more at <a href="http://github.com/jcoglan/terminus">its GitHub page</a>.
16
+ To connect the console to your browser, you&rsquo;ll need this
17
+ bookmarklet. Just drag the word &lsquo;Terminus&rsquo; up to your
18
+ bookmark toolbar and click it to connect any page to your console.</p>
19
+ </div>
20
+
21
+ <div class="footer">
22
+ <p>Copyright &copy; 2010
23
+ <a href="http://jcoglan.com">James Coglan</a>.
24
+ Released under the MIT license.</p>
25
+ </div>
26
+
27
+ </body>
28
+ </html>
29
+
metadata ADDED
@@ -0,0 +1,133 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: terminus
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - James Coglan
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2010-04-05 00:00:00 +01:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: rack
17
+ type: :runtime
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: "1.0"
24
+ version:
25
+ - !ruby/object:Gem::Dependency
26
+ name: thin
27
+ type: :runtime
28
+ version_requirement:
29
+ version_requirements: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: "1.2"
34
+ version:
35
+ - !ruby/object:Gem::Dependency
36
+ name: eventmachine
37
+ type: :runtime
38
+ version_requirement:
39
+ version_requirements: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ version: "0.12"
44
+ version:
45
+ - !ruby/object:Gem::Dependency
46
+ name: faye
47
+ type: :runtime
48
+ version_requirement:
49
+ version_requirements: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ~>
52
+ - !ruby/object:Gem::Version
53
+ version: 0.3.2
54
+ version:
55
+ - !ruby/object:Gem::Dependency
56
+ name: sinatra
57
+ type: :runtime
58
+ version_requirement:
59
+ version_requirements: !ruby/object:Gem::Requirement
60
+ requirements:
61
+ - - ">="
62
+ - !ruby/object:Gem::Version
63
+ version: "1.0"
64
+ version:
65
+ - !ruby/object:Gem::Dependency
66
+ name: packr
67
+ type: :runtime
68
+ version_requirement:
69
+ version_requirements: !ruby/object:Gem::Requirement
70
+ requirements:
71
+ - - ">="
72
+ - !ruby/object:Gem::Version
73
+ version: "3.1"
74
+ version:
75
+ - !ruby/object:Gem::Dependency
76
+ name: oyster
77
+ type: :runtime
78
+ version_requirement:
79
+ version_requirements: !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - ">="
82
+ - !ruby/object:Gem::Version
83
+ version: "0.9"
84
+ version:
85
+ description:
86
+ email: jcoglan@googlemail.com
87
+ executables:
88
+ - terminus
89
+ extensions: []
90
+
91
+ extra_rdoc_files:
92
+ - README.rdoc
93
+ files:
94
+ - README.rdoc
95
+ - lib/terminus.rb
96
+ - lib/public/style.css
97
+ - lib/public/terminus.js
98
+ - lib/public/js.packages.js
99
+ - lib/views/bookmarklet.erb
100
+ - lib/views/index.erb
101
+ - lib/terminus/server.rb
102
+ - lib/terminus/application.rb
103
+ has_rdoc: true
104
+ homepage: http://github.com/jcoglan/terminus
105
+ licenses: []
106
+
107
+ post_install_message:
108
+ rdoc_options:
109
+ - --main
110
+ - README.rdoc
111
+ require_paths:
112
+ - lib
113
+ required_ruby_version: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: "0"
118
+ version:
119
+ required_rubygems_version: !ruby/object:Gem::Requirement
120
+ requirements:
121
+ - - ">="
122
+ - !ruby/object:Gem::Version
123
+ version: "0"
124
+ version:
125
+ requirements: []
126
+
127
+ rubyforge_project:
128
+ rubygems_version: 1.3.5
129
+ signing_key:
130
+ specification_version: 3
131
+ summary: Control multiple web browsers from the command line
132
+ test_files: []
133
+