asciinema-rails 0.1.1

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.
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,2 @@
1
+ --color
2
+ --require spec_helper
@@ -0,0 +1,63 @@
1
+ require 'spec_helper'
2
+ require 'asciinema-rails'
3
+ require 'tempfile'
4
+
5
+ describe Asciinema::Rails::Convertor do
6
+ describe '.to_infile' do
7
+ context 'with invalid arguments' do
8
+ it "should raise an ArgumentError" do
9
+ expect{ Asciinema::Rails::Convertor.to_infile }.to raise_error(ArgumentError)
10
+ end
11
+ end
12
+ context 'with valid arguments' do
13
+ before do
14
+ @timing_file_location = "#{File.dirname(__FILE__)}/fixtures/sudosh-time"
15
+ @script_file_location = "#{File.dirname(__FILE__)}/fixtures/sudosh-script"
16
+ @outfile = Asciinema::Rails::Convertor.to_infile(@timing_file_location, @script_file_location, {original_terminal_cols: 180, original_terminal_rows: 40})
17
+ end
18
+ it 'returns a File or Tempfile' do
19
+ expect(@outfile).to respond_to(:close)
20
+ expect(@outfile).to respond_to(:read)
21
+ end
22
+ it 'returns file with valid JSON' do
23
+ json_output = JSON.parse(@outfile.read)
24
+
25
+ expect(json_output['version']).to eq 1
26
+ expect(json_output['width']).to eq 180
27
+ expect(json_output['height']).to eq 40
28
+ expect(json_output['stdout'].length).to eq 21
29
+ end
30
+ end
31
+ end
32
+
33
+ describe '.to_outfile' do
34
+ context 'with valid arguments' do
35
+ before do
36
+ @timing_file_location = "#{File.dirname(__FILE__)}/fixtures/sudosh-time"
37
+ @script_file_location = "#{File.dirname(__FILE__)}/fixtures/sudosh-script"
38
+
39
+ infile = Asciinema::Rails::Convertor.to_infile(@timing_file_location, @script_file_location, {original_terminal_cols: 180, original_terminal_rows: 40})
40
+ @output = Asciinema::Rails::Convertor.to_outfile(infile.path, {outfile_location: '/tmp/index.json.erb'})
41
+
42
+ end
43
+ it "returns an array of three values" do
44
+ expect(@output).to be_kind_of(Array)
45
+ expect(@output.length).to eq 3
46
+ end
47
+ it 'returns File or Tempfile as first argument' do
48
+ file = @output[0]
49
+ expect(file).to respond_to(:close)
50
+ expect(file).to respond_to(:read)
51
+ end
52
+ it 'returns valid JSON as the second argument' do
53
+ snapshot = @output[1]
54
+ expect{ JSON.parse(snapshot)}.not_to raise_error
55
+ end
56
+ it 'returns a hash as the third argument' do
57
+ attributes = @output[2]
58
+ expect(attributes).to be_kind_of(Hash)
59
+ expect(attributes.length).to eq 3
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,8 @@
1
+ ]0;@imac:~/Rails[?1034himac:Rails eddiej$ echo 'a'
2
+ a
3
+ ]0;@imac:~/Railsimac:Rails eddiej$ echo 'b'
4
+ b
5
+ ]0;@imac:~/Railsimac:Rails eddiej$ echo 'c'
6
+ c
7
+ ]0;@imac:~/Railsimac:Rails eddiej$ exit
8
+ logout
@@ -0,0 +1,22 @@
1
+ .620510 18
2
+ 0.106810 27
3
+ 2.812022 2
4
+ 0.000047 8
5
+ 0.000008 3
6
+ 0.000096 18
7
+ 0.000031 19
8
+ 2.317571 2
9
+ 0.000050 6
10
+ 0.000007 2
11
+ 0.000007 3
12
+ 0.000072 18
13
+ 0.000047 19
14
+ 1.754668 1
15
+ 0.000056 9
16
+ 0.000008 3
17
+ 0.000084 18
18
+ 0.000041 19
19
+ 1.473598 1
20
+ 0.000072 5
21
+ 0.000007 8
22
+ 0.000081 0
@@ -0,0 +1,50 @@
1
+ require 'rails/all'
2
+ # require 'rspec/rails'
3
+ # require 'rspec/autorun'
4
+
5
+ # This file was generated by the `rspec --init` command. Conventionally, all
6
+ # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
7
+ # The generated `.rspec` file contains `--require spec_helper` which will cause
8
+ # this file to always be loaded, without a need to explicitly require it in any
9
+ # files.
10
+ #
11
+ # Given that it is always loaded, you are encouraged to keep this file as
12
+ # light-weight as possible. Requiring heavyweight dependencies from this file
13
+ # will add to the boot time of your test suite on EVERY test run, even for an
14
+ # individual file that may not need all of that loaded. Instead, consider making
15
+ # a separate helper file that requires the additional dependencies and performs
16
+ # the additional setup, and require it from the spec files that actually need
17
+ # it.
18
+ #
19
+ # The `.rspec` file also contains a few flags that are not defaults but that
20
+ # users commonly want.
21
+ #
22
+ # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
23
+ RSpec.configure do |config|
24
+ # rspec-expectations config goes here. You can use an alternate
25
+ # assertion/expectation library such as wrong or the stdlib/minitest
26
+ # assertions if you prefer.
27
+ config.expect_with :rspec do |expectations|
28
+ # This option will default to `true` in RSpec 4. It makes the `description`
29
+ # and `failure_message` of custom matchers include text for helper methods
30
+ # defined using `chain`, e.g.:
31
+ # be_bigger_than(2).and_smaller_than(4).description
32
+ # # => "be bigger than 2 and smaller than 4"
33
+ # ...rather than:
34
+ # # => "be bigger than 2"
35
+ expectations.include_chain_clauses_in_custom_matcher_descriptions = true
36
+ end
37
+
38
+ # rspec-mocks config goes here. You can use an alternate test double
39
+ # library (such as bogus or mocha) by changing the `mock_with` option here.
40
+ config.mock_with :rspec do |mocks|
41
+ # Prevents you from mocking or stubbing a method that does not exist on
42
+ # a real object. This is generally recommended, and will default to
43
+ # `true` in RSpec 4.
44
+ mocks.verify_partial_doubles = true
45
+ end
46
+
47
+ config.filter_run :focus
48
+ config.run_all_when_everything_filtered = true
49
+
50
+ end
Binary file
@@ -0,0 +1,307 @@
1
+ #include <stdio.h>
2
+ #include <libtsm.h>
3
+
4
+ #define READ_BUF_SIZE 16 * 1024
5
+
6
+ static int RGB_LEVELS[6] = { 0x00, 0x5f, 0x87, 0xaf, 0xd7, 0xff };
7
+
8
+ struct tsm_screen_attr last_attr;
9
+ struct tsm_screen_attr *last_attr_ptr = &last_attr;
10
+
11
+
12
+ static int u8_wc_to_utf8(char *dest, uint32_t ch) {
13
+ if (ch < 0x80) {
14
+ dest[0] = (char)ch;
15
+ return 1;
16
+ }
17
+
18
+ if (ch < 0x800) {
19
+ dest[0] = (ch >> 6) | 0xC0;
20
+ dest[1] = (ch & 0x3F) | 0x80;
21
+ return 2;
22
+ }
23
+
24
+ if (ch < 0x10000) {
25
+ dest[0] = (ch >> 12) | 0xE0;
26
+ dest[1] = ((ch >> 6) & 0x3F) | 0x80;
27
+ dest[2] = (ch & 0x3F) | 0x80;
28
+ return 3;
29
+ }
30
+
31
+ if (ch < 0x110000) {
32
+ dest[0] = (ch >> 18) | 0xF0;
33
+ dest[1] = ((ch >> 12) & 0x3F) | 0x80;
34
+ dest[2] = ((ch >> 6) & 0x3F) | 0x80;
35
+ dest[3] = (ch & 0x3F) | 0x80;
36
+ return 4;
37
+ }
38
+
39
+ return 0;
40
+ }
41
+
42
+ void check_err(int err, const char *message) {
43
+ if (err) {
44
+ printf("%s\n", message);
45
+ exit(1);
46
+ }
47
+ }
48
+
49
+ void write_cb(struct tsm_vte *vte, const char *u8, size_t len, void *data) {}
50
+
51
+ int prepare_cb(struct tsm_screen *con, void *data) {
52
+ int *line = (int *)data;
53
+
54
+ *line = -1;
55
+ printf("[");
56
+
57
+ return 0;
58
+ }
59
+
60
+ void print_char(const uint32_t *ch) {
61
+ char str[5];
62
+ int size;
63
+
64
+ if (*ch == 0) {
65
+ printf(" ");
66
+ } else if (*ch == '"') {
67
+ printf("\\\"");
68
+ } else if (*ch == '\\') {
69
+ printf("\\\\");
70
+ } else {
71
+ size = u8_wc_to_utf8(str, *ch);
72
+ if (size > 0) {
73
+ str[size] = 0;
74
+ printf("%s", str);
75
+ } else {
76
+ printf("?");
77
+ }
78
+ }
79
+ }
80
+
81
+ int get_rgb_index(int value) {
82
+ int i;
83
+
84
+ for (i=0; i<6; i++) {
85
+ if (RGB_LEVELS[i] == value) return i;
86
+ }
87
+
88
+ return -1;
89
+ }
90
+
91
+ int get_rgb_color(int r, int g, int b) {
92
+ if (r == g && g == b && (r - 8) % 10 == 0) {
93
+ return 232 + (r - 8) / 10;
94
+ } else {
95
+ return 16 + get_rgb_index(r) * 36 + get_rgb_index(g) * 6 + get_rgb_index(b);
96
+ }
97
+ }
98
+
99
+ int get_fg(const struct tsm_screen_attr *attr) {
100
+ if (attr->fccode == -1) {
101
+ return get_rgb_color(attr->fr, attr->fg, attr->fb);
102
+ } else if (attr->fccode >= 0 && attr->fccode < 16) {
103
+ return attr->fccode;
104
+ } else {
105
+ return -1;
106
+ }
107
+ }
108
+
109
+ int get_bg(const struct tsm_screen_attr *attr) {
110
+ if (attr->bccode == -1) {
111
+ return get_rgb_color(attr->br, attr->bg, attr->bb);
112
+ } else if (attr->bccode >= 0 && attr->bccode < 16) {
113
+ return attr->bccode;
114
+ } else {
115
+ return -1;
116
+ }
117
+ }
118
+
119
+ void print_attr(const struct tsm_screen_attr *attr) {
120
+ bool comma_needed = false;
121
+ int color_code;
122
+
123
+ printf("{");
124
+
125
+ color_code = get_fg(attr);
126
+ if (color_code != -1) {
127
+ printf("\"fg\":%d", color_code);
128
+ comma_needed = true;
129
+ }
130
+
131
+ color_code = get_bg(attr);
132
+ if (color_code != -1) {
133
+ if (comma_needed) printf(",");
134
+ printf("\"bg\":%d", color_code);
135
+ comma_needed = true;
136
+ }
137
+
138
+ if (attr->bold) {
139
+ if (comma_needed) printf(",");
140
+ printf("\"bold\":true");
141
+ comma_needed = true;
142
+ }
143
+
144
+ if (attr->underline) {
145
+ if (comma_needed) printf(",");
146
+ printf("\"underline\":true");
147
+ comma_needed = true;
148
+ }
149
+
150
+ if (attr->inverse) {
151
+ if (comma_needed) printf(",");
152
+ printf("\"inverse\":true");
153
+ comma_needed = true;
154
+ }
155
+
156
+ if (attr->blink) {
157
+ if (comma_needed) printf(",");
158
+ printf("\"blink\":true");
159
+ comma_needed = true;
160
+ }
161
+
162
+ printf("}");
163
+ }
164
+
165
+ bool attr_eq(struct tsm_screen_attr *attr1, const struct tsm_screen_attr *attr2) {
166
+ return attr1->fccode == attr2->fccode &&
167
+ attr1->bccode == attr2->bccode &&
168
+ attr1->fr == attr2->fr &&
169
+ attr1->fg == attr2->fg &&
170
+ attr1->fb == attr2->fb &&
171
+ attr1->br == attr2->br &&
172
+ attr1->bg == attr2->bg &&
173
+ attr1->bb == attr2->bb &&
174
+ attr1->bold == attr2->bold &&
175
+ attr1->underline == attr2->underline &&
176
+ attr1->inverse == attr2->inverse &&
177
+ attr1->blink == attr2->blink;
178
+ }
179
+
180
+ void attr_cp(const struct tsm_screen_attr *src, struct tsm_screen_attr *dst) {
181
+ memcpy((void *)dst, (const void *)src, sizeof(last_attr));
182
+ }
183
+
184
+ void close_cell() {
185
+ printf("\",");
186
+ print_attr(last_attr_ptr);
187
+ printf("]");
188
+ }
189
+
190
+ void open_cell(const struct tsm_screen_attr *attr) {
191
+ printf("[\"");
192
+ attr_cp(attr, last_attr_ptr);
193
+ }
194
+
195
+ int draw_cb(struct tsm_screen *con, uint32_t id, const uint32_t *ch, size_t len,
196
+ unsigned int width, unsigned int posx, unsigned int posy,
197
+ const struct tsm_screen_attr *attr, void *data) {
198
+
199
+ int *line = (int *)data;
200
+
201
+ if (((signed int)posy) > *line) {
202
+ if (*line >= 0) {
203
+ close_cell();
204
+ printf("],"); // close line
205
+ }
206
+ printf("["); // open line
207
+ open_cell(attr);
208
+ }
209
+
210
+ *line = posy;
211
+
212
+ if (width == 0) return 0;
213
+
214
+ if (!(attr_eq(last_attr_ptr, attr))) {
215
+ close_cell();
216
+ printf(",");
217
+ open_cell(attr);
218
+ }
219
+
220
+ print_char(ch);
221
+
222
+ return 0;
223
+ }
224
+
225
+ int render_cb(struct tsm_screen *con, void *data) {
226
+ int *line = (int *)data;
227
+
228
+ if (*line >= 0) {
229
+ close_cell();
230
+ printf("]"); // close line
231
+ }
232
+
233
+ printf("]\n");
234
+
235
+ return 0;
236
+ }
237
+
238
+ int main(int argc, char *argv[]) {
239
+ int err;
240
+ int i;
241
+ struct tsm_screen *screen;
242
+ struct tsm_vte *vte;
243
+ int width, height;
244
+ char *line = NULL;
245
+ size_t size;
246
+ unsigned int n;
247
+ char *buffer = (char *) malloc(READ_BUF_SIZE);
248
+ int line_n, cursor_x, cursor_y;
249
+ char *cursor_visible;
250
+ unsigned int flags;
251
+ int m, read;
252
+
253
+ width = atoi(argv[1]);
254
+ height = atoi(argv[2]);
255
+
256
+ err = tsm_screen_new(&screen, NULL, NULL);
257
+ check_err(err, "can't create screen");
258
+
259
+ err = tsm_screen_resize(screen, width, height);
260
+ check_err(err, "can't resize screen");
261
+
262
+ err = tsm_vte_new(&vte, screen, write_cb, NULL, NULL, NULL);
263
+ check_err(err, "can't create vte");
264
+
265
+ for (;;) {
266
+ if (getline(&line, &size, stdin) == -1) break;
267
+
268
+ char action = line[0];
269
+
270
+ switch(action) {
271
+ case 'd':
272
+ if (getline(&line, &size, stdin) == -1) break;
273
+ n = atoi(line);
274
+ while (n > 0) {
275
+ if (n > READ_BUF_SIZE) {
276
+ m = READ_BUF_SIZE;
277
+ } else {
278
+ m = n;
279
+ }
280
+ read = fread(buffer, 1, m, stdin);
281
+ tsm_vte_input(vte, buffer, read);
282
+ n = n - read;
283
+ }
284
+ break;
285
+ case 'p':
286
+ tsm_screen_draw(screen, prepare_cb, draw_cb, render_cb, &line_n);
287
+ break;
288
+ case 'c':
289
+ cursor_x = tsm_screen_get_cursor_x(screen);
290
+ cursor_y = tsm_screen_get_cursor_y(screen);
291
+ flags = tsm_screen_get_flags(screen);
292
+ if (!(flags & TSM_SCREEN_HIDE_CURSOR)) {
293
+ cursor_visible = "true";
294
+ } else {
295
+ cursor_visible = "false";
296
+ }
297
+ printf("{\"x\":%d,\"y\":%d,\"visible\":%s}\n", cursor_x, cursor_y, cursor_visible);
298
+
299
+ break;
300
+ }
301
+
302
+ fflush(stdout);
303
+ }
304
+
305
+ return 0;
306
+ }
307
+