asciinema-rails 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +14 -0
  3. data/.rspec +2 -0
  4. data/Gemfile +9 -0
  5. data/LICENSE.txt +22 -0
  6. data/README.md +107 -0
  7. data/Rakefile +2 -0
  8. data/asciinema-rails.gemspec +27 -0
  9. data/lib/asciinema-rails.rb +1 -0
  10. data/lib/asciinema/asciicast.rb +44 -0
  11. data/lib/asciinema/asciicast_frames_file_updater.rb +24 -0
  12. data/lib/asciinema/asciicast_snapshot_updater.rb +20 -0
  13. data/lib/asciinema/brush.rb +78 -0
  14. data/lib/asciinema/cell.rb +33 -0
  15. data/lib/asciinema/cursor.rb +18 -0
  16. data/lib/asciinema/film.rb +40 -0
  17. data/lib/asciinema/frame.rb +26 -0
  18. data/lib/asciinema/frame_diff.rb +20 -0
  19. data/lib/asciinema/frame_diff_list.rb +26 -0
  20. data/lib/asciinema/grid.rb +51 -0
  21. data/lib/asciinema/json_file_writer.rb +21 -0
  22. data/lib/asciinema/rails.rb +8 -0
  23. data/lib/asciinema/rails/convertor.rb +97 -0
  24. data/lib/asciinema/rails/engine.rb +6 -0
  25. data/lib/asciinema/rails/version.rb +5 -0
  26. data/lib/asciinema/snapshot.rb +41 -0
  27. data/lib/asciinema/stdout.rb +101 -0
  28. data/lib/asciinema/terminal.rb +65 -0
  29. data/spec/.rspec +2 -0
  30. data/spec/asciinemosh_spec.rb +63 -0
  31. data/spec/fixtures/sudosh-script +8 -0
  32. data/spec/fixtures/sudosh-time +22 -0
  33. data/spec/spec_helper.rb +50 -0
  34. data/src/terminal +0 -0
  35. data/src/terminal.c +307 -0
  36. data/vendor/assets/javscripts/asciinema-rails.js +4 -0
  37. data/vendor/assets/javscripts/asciinema.org/asciinema-player.js +1149 -0
  38. data/vendor/assets/javscripts/asciinema.org/rAF.js +32 -0
  39. data/vendor/assets/javscripts/asciinema.org/react-0.10.0.js +17228 -0
  40. data/vendor/assets/javscripts/asciinema.org/screenfull.js +143 -0
  41. data/vendor/assets/stylesheets/asciinema-rails.css +4 -0
  42. data/vendor/assets/stylesheets/asciinema.org/asciinema-player.css +1732 -0
  43. data/vendor/assets/stylesheets/asciinema.org/themes/solarized-dark.css +35 -0
  44. data/vendor/assets/stylesheets/asciinema.org/themes/solarized-light.css +35 -0
  45. data/vendor/assets/stylesheets/asciinema.org/themes/tango.css +35 -0
  46. metadata +192 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: a77da9f1f221e64eff158e9a6f11bb996728c37f
4
+ data.tar.gz: c3ccee4badaeb98d419843073baf58aa5823dd1a
5
+ SHA512:
6
+ metadata.gz: 3eda26b09f2775ca6e134bf2082d376aba12c5beba1bf4b1996daa73dc5c3ae11f2c0c79991b85531a980702cb2f139fa2ab9b0ef3c63a89f870454871b1a562
7
+ data.tar.gz: f4ae9674f9b853ecbdee0880e29a4ab79f699de0e56958a16538f836a082608a24d4d65a86f44768ed81580556f283d37ef131ccb6c4f5d3b834ccede7c63b82
@@ -0,0 +1,14 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ *.bundle
11
+ *.so
12
+ *.o
13
+ *.a
14
+ mkmf.log
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format documentation
data/Gemfile ADDED
@@ -0,0 +1,9 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
4
+
5
+ gem 'railties'
6
+ gem 'activesupport'
7
+ gem 'oj'
8
+ gem 'oj_mimic_json'
9
+ gem 'yajl-ruby', '~> 1.2.1', :require => 'yajl'
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2015 Eddie Johnston
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,107 @@
1
+ # Asciinema-Rails
2
+
3
+ TLDR: Record terminal sessions from local or remote machines and play them back from your own website. See the demo at [http://eddiej.github.io/asciinema-rails](http://eddiej.github.io/asciinema-rails).
4
+
5
+ Asciinema-rails is a gem that allows you to generate playback files from Asciinema asciicasts or Sudosh log files and host them from your own website using the bundled player.
6
+
7
+ ## Installation
8
+
9
+ Add this line to your application's Gemfile:
10
+
11
+ ```ruby
12
+ gem 'asciinema-rails'
13
+ ```
14
+
15
+ And then execute:
16
+
17
+ $ bundle install
18
+
19
+ Or install it manually:
20
+
21
+ $ gem install asciinema-rails
22
+
23
+ ## Prerequisites
24
+
25
+ Asciinema-rails gem depends on the binary `terminal` which must be compiled for your platform before using this gem. The source file `terminal.c` is located in the `/src` directory of the gem. Before compiling, you need to install a suitable version of the `libtsm` library for your platform. For Darwin based systems, a reliable source is
26
+ [https://github.com/skade/libtsm](https://github.com/skade/libtsm). For other distributions, see [http://www.freedesktop.org/wiki/Software/kmscon/libtsm](http://www.freedesktop.org/wiki/Software/kmscon/libtsm).
27
+
28
+ Once `libtsm` has been installed, compile `terminal.c`:
29
+
30
+ $ gcc -O3 -o terminal terminal.c -ltsm
31
+
32
+ The resulting binary file `terminal` should be placed in your binaries folder (e.g. `/usr/bin`), or added to the $PATH environment variable.
33
+
34
+
35
+ ## Usage
36
+
37
+ Asciinema-rails consists of two parts - a Convertor module for creating player files, and an engine that provides the assets that allow you to playback the files from your own site.
38
+
39
+ ### Asciinema::Rails::Convertor
40
+
41
+ Asciinema::Rails::Convertor has two methods, one to generate player files from Asciinema asciicast files, and another to covert Sudosh log files to the Asciinema asciicast format (which can then be used to create player files.)
42
+
43
+ #### Creating playback files from asciicasts
44
+ From within your Rails application or terminal, create playback files using the `asciicast_to_playback` function:
45
+
46
+ Asciinema::Rails::Convertor.asciicast_to_playback(asciicast_path, {playback_path: '/path/to/playback/file'})
47
+
48
+ where `asciicast_path` is the path to the file created after running the Asciinema recorder and `playback_path` is the path to save the new file.
49
+
50
+ #### Creating asciicasts from Sudosh log files
51
+ To create a playback file from Sudosh log files, you must first covert them to the Asciinma asciicast format using the `sudosh_to_asciicast` function:
52
+
53
+ Asciinema::Rails::Convertor.sudosh_to_asciicast(sudosh_timing_file_path, sudosh_script_file_path, {asciicast_path: /path/to/asciicast/file})
54
+
55
+ where `asciicast_path` is the path to save the new asciicast file. If the width and height of the Susodh session terminal are available, these values can be passed in as additional parameters so that the resulting player is the original size of the terminal session. Note that Playback files can be generated from the resulting asciicast using the `asciicast_to_playback` method above.
56
+
57
+ ### Rails Engine
58
+
59
+ To display a player from your app, you need to include the asciineama-rails javascript and stylesheet files in ```app/assets/javascripts/application.js```and ```app/assets/stylesheets/application.css respectively```:
60
+
61
+ ##### application.js
62
+ //= require asciinema-rails
63
+
64
+ ##### application.css
65
+ *= require asciinema-rails
66
+
67
+ Then include the player markup and javascript in any of your views:
68
+
69
+ <div id='player-container'></div> <!-- the div that will contain the player -->
70
+
71
+ <%= javascript_tag do %>
72
+ $(function() {
73
+ function createPlayer() {
74
+ var source = new asciinema.HttpArraySource("recording.json", 1);
75
+ var snapshot = []
76
+ var movie = new asciinema.Movie(180, 43, source, snapshot, 9);
77
+
78
+ React.renderComponent(
79
+ asciinema.Player({ autoPlay: true, movie: movie }),
80
+ $('#player-container')[0]
81
+ );
82
+ }
83
+ createPlayer();
84
+ });
85
+ <% end %>
86
+
87
+ In this example, the Asciinema player file `recording.json` has been placed in the public directory of the app. This can be replaced with a fully qualified url, as long as it points to a player file.
88
+
89
+ The parameters sent to the `asciinema.Movie` constructor are the terminal width (cols), height (rows), playback file source, snapshot and playback speed.
90
+ The width, height and snapshot string are returned form the `Asciinema::Rails::Convertor.asciicast_to_playback` method. Typically, player files would be associated with a Rails model, and this information would be saved as model fields when the playback files are generated.
91
+
92
+
93
+ ## Testing
94
+ The test for the gem are written in Rspec:
95
+
96
+ bundle exec rspec spec
97
+
98
+ ## Demo
99
+ A demo of the player is available at [http://eddiej.github.io/asciinema-rails](http://eddiej.github.io/asciinema-rails). The embedded player shows a recording if the gem being installed and used in a new Rails application.
100
+
101
+ ## Contributing
102
+
103
+ 1. Fork it ( https://github.com/[my-github-username]/asciinemosh/fork )
104
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
105
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
106
+ 4. Push to the branch (`git push origin my-new-feature`)
107
+ 5. Create a new Pull Request
@@ -0,0 +1,2 @@
1
+ require "bundler/gem_tasks"
2
+
@@ -0,0 +1,27 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'asciinema/rails/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "asciinema-rails"
8
+ spec.version = Asciinema::Rails::VERSION
9
+ spec.authors = ["Eddie Johnston"]
10
+ spec.email = ["eddie@beanstalk.ie"]
11
+ spec.summary = %q{Converts sudosh log files to asciinema format. Provides JS and CSS includes to allow you host your own asciicasts.}
12
+ spec.homepage = ""
13
+ spec.license = "MIT"
14
+
15
+ spec.files = `git ls-files -z`.split("\x0")
16
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
17
+ spec.require_paths = ["lib"]
18
+
19
+ spec.add_dependency "oj"
20
+ spec.add_dependency "oj_mimic_json"
21
+ spec.add_dependency "yajl-ruby", "~> 1.2.1"
22
+
23
+ spec.add_development_dependency "bundler", "~> 1.7"
24
+ spec.add_development_dependency "rake", "~> 10.0"
25
+ spec.add_development_dependency "rspec", "~> 3"
26
+ spec.add_development_dependency "rails"
27
+ end
@@ -0,0 +1 @@
1
+ require "asciinema/rails"
@@ -0,0 +1,44 @@
1
+ require 'asciinema/terminal'
2
+ require 'asciinema/stdout'
3
+
4
+ class Asciicast
5
+
6
+
7
+ attr_reader :terminal_columns, :terminal_lines, :duration, :file, :version
8
+ attr_accessor :stdout_frames, :snapshot, :snapshot_at
9
+
10
+ def initialize(terminal_columns, terminal_lines, duration, file_location)
11
+ @terminal_columns = terminal_columns
12
+ @terminal_lines = terminal_lines
13
+ @duration = duration
14
+ @file = File.new(file_location)
15
+ @version = 1
16
+ end
17
+
18
+
19
+
20
+
21
+ def stdout
22
+ return @stdout if @stdout
23
+ @stdout = Stdout::Buffered.new(get_stdout)
24
+ end
25
+
26
+ def with_terminal
27
+ terminal = Terminal.new(terminal_columns, terminal_lines)
28
+ yield(terminal)
29
+ ensure
30
+ terminal.release if terminal
31
+ end
32
+
33
+ private
34
+
35
+ def get_stdout
36
+ if version == 0
37
+ Stdout::MultiFile.new(stdout_data.decompressed_path,
38
+ stdout_timing.decompressed_path)
39
+ else
40
+ Stdout::SingleFile.new(file.path)
41
+ end
42
+ end
43
+
44
+ end
@@ -0,0 +1,24 @@
1
+ require 'asciinema/json_file_writer'
2
+
3
+ class AsciicastFramesFileUpdater
4
+
5
+ def initialize(file_writer = JsonFileWriter.new)
6
+ @file_writer = file_writer
7
+ end
8
+
9
+ def update(asciicast, outfile_location=nil)
10
+ file = outfile_location.present? ? File.new(outfile_location, 'w') : Tempfile.new('outfile')
11
+ # file = File.new(new_outfile_location, 'w')
12
+
13
+ asciicast.with_terminal do |terminal|
14
+ film = Film.new(asciicast.stdout, terminal)
15
+ file_writer.write_enumerable(file, film.frames)
16
+ end
17
+ asciicast.stdout_frames = file
18
+ end
19
+
20
+ private
21
+
22
+ attr_reader :file_writer
23
+
24
+ end
@@ -0,0 +1,20 @@
1
+ require 'asciinema/film'
2
+
3
+
4
+ class AsciicastSnapshotUpdater
5
+
6
+ def update(asciicast, at_seconds = nil)
7
+ at_seconds ||= asciicast.snapshot_at || asciicast.duration / 2
8
+ snapshot = generate_snapshot(asciicast, at_seconds)
9
+ asciicast.snapshot = snapshot
10
+ end
11
+
12
+ private
13
+
14
+ def generate_snapshot(asciicast, at_seconds)
15
+ asciicast.with_terminal do |terminal|
16
+ Film.new(asciicast.stdout, terminal).snapshot_at(at_seconds)
17
+ end
18
+ end
19
+
20
+ end
@@ -0,0 +1,78 @@
1
+ class Brush
2
+ require 'active_support/core_ext/hash'
3
+
4
+ ALLOWED_ATTRIBUTES = [:fg, :bg, :bold, :underline, :inverse, :blink]
5
+ DEFAULT_FG_CODE = 7
6
+ DEFAULT_BG_CODE = 0
7
+
8
+ def initialize(attributes = {})
9
+ @attributes = attributes.symbolize_keys
10
+ end
11
+
12
+ def ==(other)
13
+ fg == other.fg &&
14
+ bg == other.bg &&
15
+ bold? == other.bold? &&
16
+ underline? == other.underline? &&
17
+ blink? == other.blink?
18
+ end
19
+
20
+ def fg
21
+ inverse? ? bg_code || DEFAULT_BG_CODE : fg_code
22
+ end
23
+
24
+ def bg
25
+ inverse? ? fg_code || DEFAULT_FG_CODE : bg_code
26
+ end
27
+
28
+ def bold?
29
+ !!attributes[:bold]
30
+ end
31
+
32
+ def underline?
33
+ !!attributes[:underline]
34
+ end
35
+
36
+ def inverse?
37
+ !!attributes[:inverse]
38
+ end
39
+
40
+ def blink?
41
+ !!attributes[:blink]
42
+ end
43
+
44
+ def default?
45
+ fg.nil? && bg.nil? && !bold? && !underline? && !inverse? && !blink?
46
+ end
47
+
48
+ def as_json(*)
49
+ attributes.slice(*ALLOWED_ATTRIBUTES)
50
+ end
51
+
52
+ protected
53
+
54
+ attr_reader :attributes
55
+
56
+ private
57
+
58
+ def fg_code
59
+ calculate_code(:fg, bold?)
60
+ end
61
+
62
+ def bg_code
63
+ calculate_code(:bg, blink?)
64
+ end
65
+
66
+ def calculate_code(attr_name, strong)
67
+ code = attributes[attr_name]
68
+
69
+ if code
70
+ if code < 8 && strong
71
+ code += 8
72
+ end
73
+ end
74
+
75
+ code
76
+ end
77
+
78
+ end
@@ -0,0 +1,33 @@
1
+ require 'active_support/core_ext/module/delegation.rb'
2
+
3
+ class Cell
4
+
5
+ attr_reader :text, :brush
6
+
7
+ delegate :size, :to => :text
8
+
9
+ def initialize(text, brush)
10
+ @text = text
11
+ @brush = brush
12
+ end
13
+
14
+ def empty?
15
+ text.blank? && brush.default?
16
+ end
17
+
18
+ def ==(other)
19
+ text == other.text && brush == other.brush
20
+ end
21
+
22
+ def [](*args)
23
+ self.class.new(text[*args], brush)
24
+ end
25
+
26
+ def as_json(*)
27
+ [text, brush.as_json]
28
+ end
29
+
30
+ def to_f
31
+ size
32
+ end
33
+ end
@@ -0,0 +1,18 @@
1
+ class Cursor
2
+
3
+ attr_reader :x, :y, :visible
4
+
5
+ def initialize(x, y, visible)
6
+ @x, @y, @visible = x, y, visible
7
+ end
8
+
9
+ def diff(other)
10
+ diff = {}
11
+ diff[:x] = x if other && x != other.x || other.nil?
12
+ diff[:y] = y if other && y != other.y || other.nil?
13
+ diff[:visible] = visible if other && visible != other.visible || other.nil?
14
+
15
+ diff
16
+ end
17
+
18
+ end
@@ -0,0 +1,40 @@
1
+ require 'asciinema/frame'
2
+ require 'asciinema/frame_diff_list'
3
+
4
+ class Film
5
+
6
+ def initialize(stdout, terminal)
7
+ @stdout = stdout
8
+ @terminal = terminal
9
+ end
10
+
11
+ def snapshot_at(time)
12
+ stdout_each_until(time) do |delay, data|
13
+ terminal.feed(data)
14
+ end
15
+
16
+ terminal.snapshot
17
+ end
18
+
19
+ def frames
20
+ frames = stdout.map do |delay, data|
21
+ terminal.feed(data)
22
+ [delay, Frame.new(terminal.snapshot, terminal.cursor)]
23
+ end
24
+
25
+ FrameDiffList.new(frames)
26
+ end
27
+
28
+ private
29
+
30
+ def stdout_each_until(seconds)
31
+ stdout.each do |delay, frame_data|
32
+ seconds -= delay
33
+ break if seconds <= 0
34
+ yield(delay, frame_data)
35
+ end
36
+ end
37
+
38
+ attr_reader :stdout, :terminal
39
+
40
+ end