shelr 0.9.7

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.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