shelr 0.9.7

Sign up to get free protection for your applications and to get access to all the features.
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
@@ -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,9 @@
1
+ require 'spec_helper'
2
+
3
+ describe Shelr::Publisher do
4
+ describe "#publish(id)" do
5
+ it "should publish shellcast with oauth" do
6
+ pending
7
+ end
8
+ end
9
+ end
@@ -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
@@ -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
@@ -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