shelr 0.9.7
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +5 -0
- data/.rspec +1 -0
- data/Gemfile +9 -0
- data/Gemfile.lock +31 -0
- data/LICENSE.txt +674 -0
- data/README.md +41 -0
- data/Rakefile +46 -0
- data/TODO.org +7 -0
- data/VERSION +1 -0
- data/bin/shelr +63 -0
- data/lib/shelr/player.rb +92 -0
- data/lib/shelr/publisher.rb +44 -0
- data/lib/shelr/recorder.rb +93 -0
- data/lib/shelr.rb +33 -0
- data/shelr.1 +107 -0
- data/shelr.1.ronn +63 -0
- data/spec/shelr/player_spec.rb +0 -0
- data/spec/shelr/publisher_spec.rb +9 -0
- data/spec/shelr/recorder_spec.rb +41 -0
- data/spec/shelr_spec.rb +15 -0
- data/spec/spec_helper.rb +14 -0
- metadata +128 -0
data/README.md
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
# Shelr -- screencasting for [shell ninjas][TV].
|
2
|
+
|
3
|
+
## Installation
|
4
|
+
|
5
|
+
You'll need ruby and rubygems installed.
|
6
|
+
|
7
|
+
gem install shelr
|
8
|
+
|
9
|
+
See [shellcast](http://shelr.tv/records/4d1c5458905ba77eb7000002) for details :)
|
10
|
+
|
11
|
+
## Watching other's records
|
12
|
+
|
13
|
+
shelr play http://shelr.tv/records/4d1c5458905ba77eb7000002
|
14
|
+
|
15
|
+
You can watch them online at [http://shelr.tv/][TV]
|
16
|
+
|
17
|
+
## Recording
|
18
|
+
|
19
|
+
shelr record
|
20
|
+
...
|
21
|
+
do something in your shell
|
22
|
+
..
|
23
|
+
exit
|
24
|
+
|
25
|
+
## Publishing
|
26
|
+
|
27
|
+
shelr list
|
28
|
+
<select id of your record>
|
29
|
+
shelr push <ID>
|
30
|
+
|
31
|
+
## Dependencies
|
32
|
+
|
33
|
+
You need `script` and `scriptreplay` tools from BSD Utils.
|
34
|
+
Tey are already installed if You use Debian/Ubuntu/BSD variants.
|
35
|
+
Not sure about other OSes.
|
36
|
+
|
37
|
+
## Copyright
|
38
|
+
|
39
|
+
Copyright (c) 2010 Antono Vasiljev. See LICENSE.txt for further details.
|
40
|
+
|
41
|
+
[TV]: http://shelr.tv/ "Shell records from shell ninjas"
|
data/Rakefile
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'bundler'
|
3
|
+
|
4
|
+
begin
|
5
|
+
Bundler.setup(:default, :development)
|
6
|
+
rescue Bundler::BundlerError => e
|
7
|
+
$stderr.puts e.message
|
8
|
+
$stderr.puts "Run `bundle install` to install missing gems"
|
9
|
+
exit e.status_code
|
10
|
+
end
|
11
|
+
require 'rake'
|
12
|
+
|
13
|
+
require 'jeweler'
|
14
|
+
Jeweler::Tasks.new do |gem|
|
15
|
+
gem.name = "shelr"
|
16
|
+
gem.homepage = "http://shelr.tv/"
|
17
|
+
gem.license = "GPLv3"
|
18
|
+
gem.summary = %Q{Screencasts for Shell Ninjas}
|
19
|
+
gem.description = %Q{Screencast utility for unix shell junkies :)}
|
20
|
+
gem.email = "self@antono.info"
|
21
|
+
gem.authors = ["Antono Vasiljev", "Pete Clark"]
|
22
|
+
end
|
23
|
+
Jeweler::RubygemsDotOrgTasks.new
|
24
|
+
|
25
|
+
require 'rspec/core'
|
26
|
+
require 'rspec/core/rake_task'
|
27
|
+
RSpec::Core::RakeTask.new(:spec) do |spec|
|
28
|
+
spec.pattern = FileList['spec/**/*_spec.rb']
|
29
|
+
end
|
30
|
+
|
31
|
+
RSpec::Core::RakeTask.new(:rcov) do |spec|
|
32
|
+
spec.pattern = 'spec/**/*_spec.rb'
|
33
|
+
spec.rcov = true
|
34
|
+
end
|
35
|
+
|
36
|
+
task :default => :spec
|
37
|
+
|
38
|
+
require 'rake/rdoctask'
|
39
|
+
Rake::RDocTask.new do |rdoc|
|
40
|
+
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
41
|
+
|
42
|
+
rdoc.rdoc_dir = 'rdoc'
|
43
|
+
rdoc.title = "shelr #{version}"
|
44
|
+
rdoc.rdoc_files.include('README*')
|
45
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
46
|
+
end
|
data/TODO.org
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
* DONE Remove dependencies for easier packaging
|
2
|
+
* TODO Port to termrec or ttyrec
|
3
|
+
git clone http://angband.pl/git/termrec
|
4
|
+
http://angband.pl/termrec.html
|
5
|
+
* TODO Port Figlet from artii gem to ruby 1.9.2
|
6
|
+
and use it instead hardcoded headers and footers
|
7
|
+
* TODO Think about terminal setup before shellcasting
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.9.7
|
data/bin/shelr
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
$LOAD_PATH.unshift(File.expand_path('../../lib', __FILE__))
|
4
|
+
|
5
|
+
require 'rubygems'
|
6
|
+
require 'shelr'
|
7
|
+
|
8
|
+
BASENAME = File.basename(__FILE__)
|
9
|
+
HELP = <<-HELP
|
10
|
+
|
11
|
+
Usage: #{BASENAME} command [arg]
|
12
|
+
|
13
|
+
Commands:
|
14
|
+
setup API_KEY - setup
|
15
|
+
record - record new shellcast
|
16
|
+
push RECORD_ID - publish
|
17
|
+
list - print list of records
|
18
|
+
play RECORD_ID - play local record
|
19
|
+
play RECORD_URL - play remote record
|
20
|
+
|
21
|
+
Visit: http://shelr.tv/ for more info.
|
22
|
+
|
23
|
+
HELP
|
24
|
+
|
25
|
+
case ARGV[0]
|
26
|
+
when '-h', '--help'
|
27
|
+
puts HELP
|
28
|
+
when 'record'
|
29
|
+
Shelr::Recorder.record!
|
30
|
+
when 'list'
|
31
|
+
Shelr::Player.list
|
32
|
+
when 'play'
|
33
|
+
if ARGV[1]
|
34
|
+
if ARGV[1] =~ /^http:.*/
|
35
|
+
Shelr::Player.play_remote(ARGV[1])
|
36
|
+
else
|
37
|
+
Shelr::Player.play(ARGV[1])
|
38
|
+
end
|
39
|
+
else
|
40
|
+
puts "Missing id for shellcast"
|
41
|
+
Shelr::Player.list
|
42
|
+
puts "Select one..."
|
43
|
+
exit
|
44
|
+
end
|
45
|
+
when 'push'
|
46
|
+
if ARGV[1]
|
47
|
+
Shelr::Publisher.new.publish(ARGV[1])
|
48
|
+
else
|
49
|
+
puts "What do you want to publish?"
|
50
|
+
Shelr::Player.list
|
51
|
+
puts "Select one..."
|
52
|
+
exit
|
53
|
+
end
|
54
|
+
when 'setup'
|
55
|
+
if ARGV[1]
|
56
|
+
Shelr.api_key = ARGV[1]
|
57
|
+
else
|
58
|
+
puts "\n\tUsage: #{BASENAME} setup API_KEY\n\n"
|
59
|
+
exit
|
60
|
+
end
|
61
|
+
else
|
62
|
+
puts HELP
|
63
|
+
end
|
data/lib/shelr/player.rb
ADDED
@@ -0,0 +1,92 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require 'net/http'
|
3
|
+
require 'tmpdir'
|
4
|
+
require 'fileutils'
|
5
|
+
require 'pathname'
|
6
|
+
|
7
|
+
module Shelr
|
8
|
+
class Player
|
9
|
+
|
10
|
+
HEADER = <<-EOH
|
11
|
+
|
12
|
+
____ _ _ _ ____ _
|
13
|
+
/ ___|| |__ ___| | |/ ___|__ _ ___| |_
|
14
|
+
\___ \| '_ \ / _ \ | | | / _` / __| __|
|
15
|
+
___) | | | | __/ | | |__| (_| \__ \ |_
|
16
|
+
|____/|_| |_|\___|_|_|\____\__,_|___/\__|
|
17
|
+
|
18
|
+
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
|
19
|
+
|
20
|
+
|
21
|
+
EOH
|
22
|
+
|
23
|
+
FOOTER = <<-EOF
|
24
|
+
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
|
25
|
+
_____ _ _____ _
|
26
|
+
|_ _| |__ ___ | ____|_ __ __| |
|
27
|
+
| | | '_ \ / _ \ | _| | '_ \ / _` |
|
28
|
+
| | | | | | __/ | |___| | | | (_| |
|
29
|
+
|_| |_| |_|\___| |_____|_| |_|\__,_|
|
30
|
+
|
31
|
+
EOF
|
32
|
+
|
33
|
+
|
34
|
+
def self.play(id)
|
35
|
+
new(id).play
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.play_remote(url)
|
39
|
+
puts ".==> Fetching #{url}"
|
40
|
+
resp = Net::HTTP.get(URI.parse(url))
|
41
|
+
parts = JSON.parse(resp)
|
42
|
+
|
43
|
+
print "Title:\t"
|
44
|
+
puts parts['title']
|
45
|
+
print "Description:\t"
|
46
|
+
puts parts['description']
|
47
|
+
|
48
|
+
Dir.mktmpdir do |dir|
|
49
|
+
%w(typescript timing).each do |type|
|
50
|
+
File.open(File.join(dir, type), 'w') { |f| f.puts(parts[type]) }
|
51
|
+
end
|
52
|
+
puts "+==> Playing... "
|
53
|
+
system "scriptreplay #{File.join(dir, 'timing')} #{File.join(dir, 'typescript')}"
|
54
|
+
puts "+\n+"
|
55
|
+
print "| Title:\t"
|
56
|
+
puts parts['title']
|
57
|
+
print "| Description:\t"
|
58
|
+
puts parts['description']
|
59
|
+
puts "`==> The end... "
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def self.list
|
64
|
+
Dir[File.join(Shelr::DATA_DIR, "**", 'meta')].each do |path|
|
65
|
+
metadata = JSON.parse(IO.read(path))
|
66
|
+
puts "#{metadata["created_at"]}: #{metadata["title"]}"
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def initialize(id)
|
71
|
+
@record_id = id
|
72
|
+
end
|
73
|
+
|
74
|
+
def play
|
75
|
+
puts HEADER
|
76
|
+
puts
|
77
|
+
system(scriptreplay_cmd)
|
78
|
+
puts
|
79
|
+
puts FOOTER
|
80
|
+
end
|
81
|
+
|
82
|
+
private
|
83
|
+
|
84
|
+
def record_file(name)
|
85
|
+
File.join(Shelr.data_dir(@record_id), name)
|
86
|
+
end
|
87
|
+
|
88
|
+
def scriptreplay_cmd
|
89
|
+
"scriptreplay #{record_file('timing')} #{record_file('typescript')}"
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'net/http'
|
2
|
+
require 'uri'
|
3
|
+
|
4
|
+
module Shelr
|
5
|
+
class Publisher
|
6
|
+
|
7
|
+
def publish(id)
|
8
|
+
uri = URI.parse(Shelr::API_URL + '/records')
|
9
|
+
params = { 'record' => prepare(id) }
|
10
|
+
params.merge!({'api_key' => Shelr.api_key}) if api_key
|
11
|
+
res = Net::HTTP.post_form(uri, params)
|
12
|
+
res = JSON.parse(res.body)
|
13
|
+
if res['ok']
|
14
|
+
puts res['message']
|
15
|
+
puts Shelr::API_URL + '/records/' + res['id']
|
16
|
+
else
|
17
|
+
puts res['message']
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def api_key
|
22
|
+
unless Shelr.api_key
|
23
|
+
print 'Paste your API KEY [or Enter to publish as Anonymous]: '
|
24
|
+
key = STDIN.gets.strip
|
25
|
+
Shelr.api_key = key unless key.empty?
|
26
|
+
end
|
27
|
+
Shelr.api_key
|
28
|
+
end
|
29
|
+
|
30
|
+
def prepare(id)
|
31
|
+
out = {}
|
32
|
+
['meta', 'timing', 'typescript'].each do |file|
|
33
|
+
out[file] = File.read(File.join(Shelr.data_dir(id), file))
|
34
|
+
end
|
35
|
+
meta = JSON.parse(out.delete('meta'))
|
36
|
+
meta.each { |k,v| out[k] = v }
|
37
|
+
print 'Description: '
|
38
|
+
out['description'] = STDIN.gets.strip
|
39
|
+
print 'Tags (ex: howto, linux): '
|
40
|
+
out['tags'] = STDIN.gets.strip
|
41
|
+
return out.to_json
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
module Shelr
|
3
|
+
class Recorder
|
4
|
+
|
5
|
+
HEADER = <<-EOH
|
6
|
+
____ _ _
|
7
|
+
| _ \ ___ ___ ___ _ __ __| (_)_ __ __ _
|
8
|
+
| |_) / _ \/ __/ _ \| '__/ _` | | '_ \ / _` |
|
9
|
+
| _ < __/ (_| (_) | | | (_| | | | | | (_| |
|
10
|
+
|_| \_\___|\___\___/|_| \__,_|_|_| |_|\__, |
|
11
|
+
|___/
|
12
|
+
EOH
|
13
|
+
|
14
|
+
FOOTER = <<-EOF
|
15
|
+
_____ _ _ _ _
|
16
|
+
| ___(_)_ __ (_)___| |__ ___ __| |
|
17
|
+
| |_ | | '_ \| / __| '_ \ / _ \/ _` |
|
18
|
+
| _| | | | | | \__ \ | | | __/ (_| |
|
19
|
+
|_| |_|_| |_|_|___/_| |_|\___|\__,_|
|
20
|
+
|
21
|
+
EOF
|
22
|
+
|
23
|
+
def self.record!
|
24
|
+
new.record!
|
25
|
+
end
|
26
|
+
|
27
|
+
def initialize
|
28
|
+
@meta = {}
|
29
|
+
end
|
30
|
+
|
31
|
+
def record!
|
32
|
+
check_record_dir
|
33
|
+
request_metadata
|
34
|
+
puts HEADER
|
35
|
+
puts "Your session started"
|
36
|
+
puts "Type Ctrl+D or exit to finish recording"
|
37
|
+
system(script_cmd)
|
38
|
+
restore_terminal
|
39
|
+
puts FOOTER
|
40
|
+
puts
|
41
|
+
puts "hint $ #{Shelr::APP_NAME} play #{record_id}"
|
42
|
+
puts "hint $ #{Shelr::APP_NAME} push #{record_id}"
|
43
|
+
end
|
44
|
+
|
45
|
+
def request_metadata
|
46
|
+
init_terminal
|
47
|
+
print "Provide HUMAN NAME for Your shellcast: "
|
48
|
+
@meta["title"] = STDIN.gets.strip
|
49
|
+
@meta["created_at"] = record_id
|
50
|
+
@meta["columns"] = @user_columns
|
51
|
+
@meta["rows"] = @user_rows
|
52
|
+
puts record_file('meta')
|
53
|
+
File.open(record_file('meta'), 'w+') do |meta|
|
54
|
+
meta.puts @meta.to_json
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
private
|
59
|
+
|
60
|
+
def check_record_dir
|
61
|
+
FileUtils.mkdir_p(record_dir) unless File.exists?(record_dir)
|
62
|
+
end
|
63
|
+
|
64
|
+
def init_terminal
|
65
|
+
stty_data = `stty -a`
|
66
|
+
@user_columns = stty_data.match(/columns (\d+)/)[1]
|
67
|
+
@user_rows = stty_data.match(/rows (\d+)/)[1]
|
68
|
+
puts "Saved terminal size #{@user_columns}X#{@user_rows}"
|
69
|
+
# system("stty columns 80 rows 24")
|
70
|
+
end
|
71
|
+
|
72
|
+
def restore_terminal
|
73
|
+
# system("stty columns #{@user_columns} rows #{@user_rows}")
|
74
|
+
end
|
75
|
+
|
76
|
+
def record_dir
|
77
|
+
@record_dir ||= Shelr.data_dir(record_id)
|
78
|
+
end
|
79
|
+
|
80
|
+
def record_id
|
81
|
+
@record_id ||= Time.now.to_i.to_s
|
82
|
+
end
|
83
|
+
|
84
|
+
def record_file(name)
|
85
|
+
File.join(Shelr.data_dir(record_id), name)
|
86
|
+
end
|
87
|
+
|
88
|
+
def script_cmd
|
89
|
+
"script -c 'bash' #{record_file('typescript')} -t 2> #{record_file('timing')}"
|
90
|
+
end
|
91
|
+
|
92
|
+
end
|
93
|
+
end
|
data/lib/shelr.rb
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
require 'yaml'
|
3
|
+
require 'json'
|
4
|
+
|
5
|
+
module Shelr
|
6
|
+
|
7
|
+
APP_NAME = 'shelr'
|
8
|
+
DATA_DIR = File.join(ENV['HOME'], '.share', APP_NAME)
|
9
|
+
CONFIG_DIR = File.join(ENV['HOME'], '.config', APP_NAME)
|
10
|
+
API_KEY = File.join(CONFIG_DIR, 'api_key')
|
11
|
+
API_URL = ENV['SHELR_LOCAL'] ? 'http://localhost:3000' : 'http://shelr.tv'
|
12
|
+
|
13
|
+
autoload :Recorder, 'shelr/recorder.rb'
|
14
|
+
autoload :Player, 'shelr/player.rb'
|
15
|
+
autoload :Publisher, 'shelr/publisher.rb'
|
16
|
+
|
17
|
+
class << self
|
18
|
+
def api_key
|
19
|
+
return false unless File.exist?(API_KEY)
|
20
|
+
@api_key ||= File.read(API_KEY).strip
|
21
|
+
end
|
22
|
+
|
23
|
+
def api_key=(key)
|
24
|
+
FileUtils.mkdir_p(CONFIG_DIR) unless File.exist?(CONFIG_DIR)
|
25
|
+
File.open(API_KEY, 'w+') { |f| f.puts(key.strip) }
|
26
|
+
end
|
27
|
+
|
28
|
+
def data_dir(record_id)
|
29
|
+
File.join(Shelr::DATA_DIR, record_id.to_s)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
data/shelr.1
ADDED
@@ -0,0 +1,107 @@
|
|
1
|
+
.\" generated with Ronn/v0.7.3
|
2
|
+
.\" http://github.com/rtomayko/ronn/tree/0.7.3
|
3
|
+
.
|
4
|
+
.TH "SHELR" "1" "August 2011" "" ""
|
5
|
+
.
|
6
|
+
.SH "NAME"
|
7
|
+
\fBshelr\fR \- screencasting for shell junkies
|
8
|
+
.
|
9
|
+
.SH "SYNOPSIS"
|
10
|
+
\fBshelr\fR command [id]
|
11
|
+
.
|
12
|
+
.SH "DESCRIPTION"
|
13
|
+
\fBShelr\fR records terminal output and can replay it
|
14
|
+
.
|
15
|
+
.SH "COMMANDS"
|
16
|
+
.
|
17
|
+
.TP
|
18
|
+
\fBrecord\fR
|
19
|
+
Will record your terminal unless you type \fBexit\fR or Ctrl+D and store it to $HOME/\.local/share/shellcast/
|
20
|
+
.
|
21
|
+
.TP
|
22
|
+
\fBlist\fR
|
23
|
+
lists all your shellcasts\.
|
24
|
+
.
|
25
|
+
.TP
|
26
|
+
\fBplay\fR
|
27
|
+
plays local or remote shellcast\.
|
28
|
+
.
|
29
|
+
.TP
|
30
|
+
\fBpush\fR
|
31
|
+
publish your shellcast
|
32
|
+
.
|
33
|
+
.SH "EXAMPLES"
|
34
|
+
Record your shellcast:
|
35
|
+
.
|
36
|
+
.IP "" 4
|
37
|
+
.
|
38
|
+
.nf
|
39
|
+
|
40
|
+
$ shelr record
|
41
|
+
\.\.\. do something \.\.\.
|
42
|
+
$ exit
|
43
|
+
.
|
44
|
+
.fi
|
45
|
+
.
|
46
|
+
.IP "" 0
|
47
|
+
.
|
48
|
+
.P
|
49
|
+
List recorded shellcasts:
|
50
|
+
.
|
51
|
+
.IP "" 4
|
52
|
+
.
|
53
|
+
.nf
|
54
|
+
|
55
|
+
$ shelr list
|
56
|
+
.
|
57
|
+
.fi
|
58
|
+
.
|
59
|
+
.IP "" 0
|
60
|
+
.
|
61
|
+
.P
|
62
|
+
Play local shellcast:
|
63
|
+
.
|
64
|
+
.IP "" 4
|
65
|
+
.
|
66
|
+
.nf
|
67
|
+
|
68
|
+
$ shelr play 1293702847
|
69
|
+
.
|
70
|
+
.fi
|
71
|
+
.
|
72
|
+
.IP "" 0
|
73
|
+
.
|
74
|
+
.P
|
75
|
+
Play remote shellcast:
|
76
|
+
.
|
77
|
+
.IP "" 4
|
78
|
+
.
|
79
|
+
.nf
|
80
|
+
|
81
|
+
$ shelr play http://shelr\.tv/records/4d1f7c3890820d6144000002\.json
|
82
|
+
.
|
83
|
+
.fi
|
84
|
+
.
|
85
|
+
.IP "" 0
|
86
|
+
.
|
87
|
+
.P
|
88
|
+
Publish your shellcast:
|
89
|
+
.
|
90
|
+
.IP "" 4
|
91
|
+
.
|
92
|
+
.nf
|
93
|
+
|
94
|
+
$ shelr push 1293702847
|
95
|
+
.
|
96
|
+
.fi
|
97
|
+
.
|
98
|
+
.IP "" 0
|
99
|
+
.
|
100
|
+
.SH "BUGS"
|
101
|
+
\fBshelr\fR is written in Ruby and depends on \fBscript\fR and \fBscriptreplay\fR, commands libraries that are non\-trivial to install on some systems\.
|
102
|
+
.
|
103
|
+
.SH "COPYRIGHT"
|
104
|
+
Shelr is Copyright (C) 2011 Antono Vasiljev \fIhttp://antono\.info/\fR
|
105
|
+
.
|
106
|
+
.SH "SEE ALSO"
|
107
|
+
script(1), scriptreplay(1)
|
data/shelr.1.ronn
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
shelr(1) -- screencasting for shell junkies
|
2
|
+
===============================================
|
3
|
+
|
4
|
+
## SYNOPSIS
|
5
|
+
|
6
|
+
`shelr` command [id]
|
7
|
+
|
8
|
+
## DESCRIPTION
|
9
|
+
|
10
|
+
**Shelr** records terminal output and can replay it
|
11
|
+
|
12
|
+
## COMMANDS
|
13
|
+
|
14
|
+
* `record`:
|
15
|
+
Will record your terminal unless you type `exit` or Ctrl+D and
|
16
|
+
store it to $HOME/.local/share/shellcast/
|
17
|
+
|
18
|
+
* `list`:
|
19
|
+
lists all your shellcasts.
|
20
|
+
|
21
|
+
* `play`:
|
22
|
+
plays local or remote shellcast.
|
23
|
+
|
24
|
+
* `push`:
|
25
|
+
publish your shellcast
|
26
|
+
|
27
|
+
## EXAMPLES
|
28
|
+
|
29
|
+
Record your shellcast:
|
30
|
+
|
31
|
+
$ shelr record
|
32
|
+
... do something ...
|
33
|
+
$ exit
|
34
|
+
|
35
|
+
List recorded shellcasts:
|
36
|
+
|
37
|
+
$ shelr list
|
38
|
+
|
39
|
+
Play local shellcast:
|
40
|
+
|
41
|
+
$ shelr play 1293702847
|
42
|
+
|
43
|
+
Play remote shellcast:
|
44
|
+
|
45
|
+
$ shelr play http://shelr.tv/records/4d1f7c3890820d6144000002.json
|
46
|
+
|
47
|
+
Publish your shellcast:
|
48
|
+
|
49
|
+
$ shelr push 1293702847
|
50
|
+
|
51
|
+
|
52
|
+
## BUGS
|
53
|
+
|
54
|
+
**shelr** is written in Ruby and depends on `script` and `scriptreplay`,
|
55
|
+
commands libraries that are non-trivial to install on some systems.
|
56
|
+
|
57
|
+
## COPYRIGHT
|
58
|
+
|
59
|
+
Shelr is Copyright (C) 2011 Antono Vasiljev <http://antono.info/>
|
60
|
+
|
61
|
+
## SEE ALSO
|
62
|
+
|
63
|
+
script(1), scriptreplay(1)
|
File without changes
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Shelr::Recorder do
|
4
|
+
|
5
|
+
before(:each) do
|
6
|
+
STDIN.stubs(:gets).returns('my shellcast')
|
7
|
+
end
|
8
|
+
|
9
|
+
describe "#record!" do
|
10
|
+
|
11
|
+
before(:each) do
|
12
|
+
subject.stubs(:system).with(anything).returns(true)
|
13
|
+
end
|
14
|
+
|
15
|
+
it "should set tty to 80x24" do
|
16
|
+
subject.expects("system").with(regexp_matches(/stty columns 80 rows 24/))
|
17
|
+
subject.record!
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should restore tty to user defined values" do
|
21
|
+
subject.expects(:restore_terminal)
|
22
|
+
subject.record!
|
23
|
+
end
|
24
|
+
|
25
|
+
it "should start script session" do
|
26
|
+
subject.expects("system").with(regexp_matches Regexp.compile("script -c 'bash'"))
|
27
|
+
subject.record!
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
describe "#restore_terminal" do
|
32
|
+
it "should call tty with saved dimensions" do
|
33
|
+
subject.expects(:system).with(
|
34
|
+
all_of(regexp_matches(/stty/),
|
35
|
+
regexp_matches(/columns/),
|
36
|
+
regexp_matches(/rows/))
|
37
|
+
)
|
38
|
+
subject.send :restore_terminal
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
data/spec/shelr_spec.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Shelr do
|
4
|
+
it "should have ::APP_NAME defined" do
|
5
|
+
Shelr::APP_NAME.should == 'shelr'
|
6
|
+
end
|
7
|
+
|
8
|
+
it "should provide XDG and APP_NAME based ::DATA_DIR" do
|
9
|
+
Shelr::DATA_DIR.should == File.join(XDG['DATA_HOME'].to_s, Shelr::APP_NAME)
|
10
|
+
end
|
11
|
+
|
12
|
+
it "should provide XDG config path" do
|
13
|
+
Shelr::CONFIG_DIR.should == File.join(XDG['CONFIG_HOME'].to_s, Shelr::APP_NAME)
|
14
|
+
end
|
15
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
2
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
3
|
+
require 'rspec'
|
4
|
+
require 'shelr'
|
5
|
+
require "rubygems"
|
6
|
+
require "bundler/setup"
|
7
|
+
|
8
|
+
# Requires supporting files with custom matchers and macros, etc,
|
9
|
+
# in ./support/ and its subdirectories.
|
10
|
+
Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
|
11
|
+
|
12
|
+
RSpec.configure do |config|
|
13
|
+
config.mock_with :mocha
|
14
|
+
end
|