tkn2 0.0.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.
- checksums.yaml +7 -0
- data/.gitignore +17 -0
- data/.ruby-version +1 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +148 -0
- data/Rakefile +1 -0
- data/examples/constant_autoloading_in_ruby_on_rails.rb +397 -0
- data/examples/simple.rb +23 -0
- data/lib/tkn2.rb +15 -0
- data/lib/tkn2/ansi_reader.rb +71 -0
- data/lib/tkn2/content_block.rb +17 -0
- data/lib/tkn2/deck.rb +59 -0
- data/lib/tkn2/screen.rb +48 -0
- data/lib/tkn2/slide.rb +50 -0
- data/lib/tkn2/utils.rb +11 -0
- data/lib/tkn2/version.rb +3 -0
- data/tkn2.gemspec +25 -0
- metadata +103 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 2c47a9bc8f71f08d678661bd99720f72c2fdc065
|
4
|
+
data.tar.gz: 5700de099467bd9c1c45166a6ecdf7c189e1e474
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: be808afbf8b891c5f58e59f712829a85498a80ff9758b78797e149f3ef5c1280bd32a957c00dd475258be2ceb2c4ccf32cfb6601b8d120933dad87133fbac9c8
|
7
|
+
data.tar.gz: 41a2b35ebfef372eceb17213c39324cae3501e458c86521d8214fabcbe23dab3ffb683f47ff4caeb2bb9e065b1368a55e4f3eb22367f66c541812f35e8203a98
|
data/.gitignore
ADDED
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
2.0.0-p247
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2013 Sergio Gil
|
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.
|
data/README.md
ADDED
@@ -0,0 +1,148 @@
|
|
1
|
+
# Tkn2
|
2
|
+
|
3
|
+
Tkn2 is a presentation tool for the terminal. It's heavily inspired in [Xavier Noria](http://www.hashref.com/)'s
|
4
|
+
[tkn](https://github.com/fxn/tkn) (Terminal Keynote).
|
5
|
+
|
6
|
+
See [usage](#usage).
|
7
|
+
|
8
|
+
Tkn2 is work in progress. First goal is compatibility with tkn (it's almost there) and, from there, keep building
|
9
|
+
interesting features. Although the initial idea is to clone it, it might drop complete compatibility in some future
|
10
|
+
point.
|
11
|
+
|
12
|
+
Main differences with tkn:
|
13
|
+
|
14
|
+
* The commands are not available in the general namespace; you need to wrap the presentation in a `Tkn2.deck` block
|
15
|
+
* It's packed as a gem, and intended to use with bundler via a `Gemfile`. That may sound like overkill but, with tkn2
|
16
|
+
evolving, it's nice to have each presentation stored with a explicit reference to the verion of tkn2 it was written
|
17
|
+
for (tkn solves this having the script vendored)
|
18
|
+
* It uses [ncurses](http://www.gnu.org/software/ncurses/) instead of direct printing. This gets it some nice behaviour
|
19
|
+
like autoredrawing when resizing windows or changing font size
|
20
|
+
* It aims to have the code better split and organized, in order to allow better extensibility and an easier time when
|
21
|
+
adding features in the future. This is, however, a claim to be proofed.
|
22
|
+
|
23
|
+
## Usage
|
24
|
+
|
25
|
+
Have a `.rb` file requiring `tkn2` with a `Tkn2.deck` block. In that block you can use all the available commands.
|
26
|
+
You'll run that file to start the presentation
|
27
|
+
|
28
|
+
```ruby
|
29
|
+
require 'tkn2'
|
30
|
+
|
31
|
+
Tkn2.deck do
|
32
|
+
# here goes your presentation
|
33
|
+
end
|
34
|
+
```
|
35
|
+
|
36
|
+
The easiest way to do this is to have a `Gemfile` like this:
|
37
|
+
|
38
|
+
```ruby
|
39
|
+
source 'https://rubygems.org'
|
40
|
+
|
41
|
+
gem 'tkn2'
|
42
|
+
```
|
43
|
+
|
44
|
+
And then run the presentation using bundler:
|
45
|
+
|
46
|
+
```
|
47
|
+
$ bundle exec ruby mypresentation.rb
|
48
|
+
```
|
49
|
+
|
50
|
+
### Available commands
|
51
|
+
|
52
|
+
Each command generates a slide. These are the commands available:
|
53
|
+
|
54
|
+
#### `center`
|
55
|
+
|
56
|
+
Generates a slide with the given text, centered line by line.
|
57
|
+
|
58
|
+
```ruby
|
59
|
+
center <<-EOS
|
60
|
+
Tkn2 Less is more
|
61
|
+
EOS
|
62
|
+
```
|
63
|
+
|
64
|
+
#### `block`
|
65
|
+
|
66
|
+
Generates a slide with the given text, centered as a whole (useful for bulleted lists).
|
67
|
+
|
68
|
+
```ruby
|
69
|
+
block <<-EOS
|
70
|
+
This is why Tkn2 is cool:
|
71
|
+
* Reason number 1
|
72
|
+
* Reason number 2
|
73
|
+
EOS
|
74
|
+
```
|
75
|
+
|
76
|
+
#### `code`
|
77
|
+
|
78
|
+
Highlights the code given (using [pygments.rb](https://github.com/tmm1/pygments.rb) and
|
79
|
+
[pygments](http://pygments.org/)) and displays it centered.
|
80
|
+
|
81
|
+
```ruby
|
82
|
+
code <<-RUBY
|
83
|
+
puts 'hello'
|
84
|
+
RUBY
|
85
|
+
```
|
86
|
+
|
87
|
+
#### `section`
|
88
|
+
|
89
|
+
Generates a slide with the section title centered and proceeds with the contained slides.
|
90
|
+
|
91
|
+
```ruby
|
92
|
+
section 'This is a section' do
|
93
|
+
center 'Slide n. 1'
|
94
|
+
center 'Slide n. 2'
|
95
|
+
end
|
96
|
+
```
|
97
|
+
|
98
|
+
See the [`examples/`](examples/) directory.
|
99
|
+
|
100
|
+
Apart from that, it's Ruby! So you're not limited to use string literals to generate the content and you can do whatever
|
101
|
+
you want (read from the network, make calculations, etc.).
|
102
|
+
|
103
|
+
### Keys
|
104
|
+
|
105
|
+
These are the keys you can use to navigate the presentation:
|
106
|
+
|
107
|
+
* Down, Right, Enter, Space, Page Down, N: next slide
|
108
|
+
* Up, Left, Backspace, Page Up, P: previous slide
|
109
|
+
* Home: first slide
|
110
|
+
* Q: quit
|
111
|
+
|
112
|
+
## TODO / Roadmap
|
113
|
+
|
114
|
+
I'm writing Tkn2 as a way to experiment. This may make the roadmap a bit strange, as I'll be trying to bring to the
|
115
|
+
terminal the most features common in graphical presentation software, even when it will be difficult and not very practical in many cases.
|
116
|
+
|
117
|
+
That said:
|
118
|
+
|
119
|
+
* (Auto)reload [1]
|
120
|
+
* Images [1]
|
121
|
+
* Better control of *overflow* (currently the presentation crashes if the content doesn't fit in the screen). I'm
|
122
|
+
playing with the idea of having *scroll*, something usually not present in graphical presentation software and that
|
123
|
+
can be useful to present code without using a tiny font size
|
124
|
+
* A generator
|
125
|
+
* Colors and themes
|
126
|
+
* Plugin system. More as an exercise, I want to try to keep the core small and simple and implement features as plugins
|
127
|
+
* A way to compose slides with more than one element. Tools to generate content (ASCII art diagrams, etc.)
|
128
|
+
* Speaker's notes (with next slide, etc.). The only practical way of doing this I see is implementing a *server* and
|
129
|
+
having two different *clients*. This *client-server* mode is an interesting feature in itself, for remote
|
130
|
+
presentations through the network
|
131
|
+
|
132
|
+
[1] Already present in tkn.
|
133
|
+
|
134
|
+
## Thanks
|
135
|
+
|
136
|
+
* [Xavi](http://www.hashref.com/), for the great inspiration and brilliant hack
|
137
|
+
|
138
|
+
## Contributing
|
139
|
+
|
140
|
+
1. Fork it
|
141
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
142
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
143
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
144
|
+
5. Create new Pull Request
|
145
|
+
|
146
|
+
## License
|
147
|
+
|
148
|
+
Released under the MIT License, Copyright (c) 2013 Sergio Gil.
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
@@ -0,0 +1,397 @@
|
|
1
|
+
require 'tkn2'
|
2
|
+
|
3
|
+
Tkn2.deck do
|
4
|
+
center <<-EOS
|
5
|
+
Constant Autoloading in Ruby on Rails
|
6
|
+
|
7
|
+
|
8
|
+
Xavier Noria
|
9
|
+
@fxn
|
10
|
+
|
11
|
+
BaRuCo 2012
|
12
|
+
EOS
|
13
|
+
|
14
|
+
code <<-EOS
|
15
|
+
require 'application_controller'
|
16
|
+
require 'post'
|
17
|
+
|
18
|
+
class PostsController < ApplicationController
|
19
|
+
def index
|
20
|
+
@posts = Post.all
|
21
|
+
end
|
22
|
+
end
|
23
|
+
EOS
|
24
|
+
|
25
|
+
code <<-EOS
|
26
|
+
class PostsController < ApplicationController
|
27
|
+
def index
|
28
|
+
@posts = Post.all
|
29
|
+
end
|
30
|
+
end
|
31
|
+
EOS
|
32
|
+
|
33
|
+
section "Constants Refresher" do
|
34
|
+
code <<-EOS
|
35
|
+
X = 1
|
36
|
+
EOS
|
37
|
+
|
38
|
+
code <<-EOS
|
39
|
+
class C
|
40
|
+
end
|
41
|
+
EOS
|
42
|
+
|
43
|
+
code <<-EOS
|
44
|
+
# ordinary class definition
|
45
|
+
class C < D
|
46
|
+
include M
|
47
|
+
end
|
48
|
+
|
49
|
+
# equivalent modulus details
|
50
|
+
C = Class.new(D) do
|
51
|
+
include M
|
52
|
+
end
|
53
|
+
|
54
|
+
# class name comes from the constant
|
55
|
+
C.name # => "C"
|
56
|
+
EOS
|
57
|
+
|
58
|
+
code <<-EOS
|
59
|
+
ArgumentError FalseClass
|
60
|
+
Array Fiber
|
61
|
+
BasicObject File
|
62
|
+
Bignum FileTest
|
63
|
+
Binding Fixnum
|
64
|
+
Class Float
|
65
|
+
Comparable GC
|
66
|
+
Complex Gem
|
67
|
+
Config Hash
|
68
|
+
Dir IO
|
69
|
+
Encoding IOError
|
70
|
+
EncodingError IndexError
|
71
|
+
Enumerable Integer
|
72
|
+
Enumerator Interrupt
|
73
|
+
Errno Kernel
|
74
|
+
Exception ...
|
75
|
+
EOS
|
76
|
+
|
77
|
+
center <<-EOS
|
78
|
+
Constants are stored in modules
|
79
|
+
EOS
|
80
|
+
|
81
|
+
code <<-EOS
|
82
|
+
# rubinius/kernel/common/module.rb
|
83
|
+
|
84
|
+
class Module
|
85
|
+
attr_reader :constant_table
|
86
|
+
attr_writer :method_table
|
87
|
+
...
|
88
|
+
end
|
89
|
+
EOS
|
90
|
+
|
91
|
+
code <<-EOS
|
92
|
+
module M
|
93
|
+
X = 1
|
94
|
+
end
|
95
|
+
EOS
|
96
|
+
|
97
|
+
code <<-EOS
|
98
|
+
# ordinary class definition in namespace
|
99
|
+
module XML
|
100
|
+
class SAXParser
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
# equivalent modulus details to
|
105
|
+
module XML
|
106
|
+
SAXParser = Class.new
|
107
|
+
end
|
108
|
+
EOS
|
109
|
+
|
110
|
+
center <<-EOS
|
111
|
+
Constants API
|
112
|
+
EOS
|
113
|
+
|
114
|
+
center <<-EOS
|
115
|
+
Constant Name Resolution (1)
|
116
|
+
EOS
|
117
|
+
|
118
|
+
code <<-EOS
|
119
|
+
module M
|
120
|
+
X = 1
|
121
|
+
end
|
122
|
+
EOS
|
123
|
+
|
124
|
+
code <<-EOS
|
125
|
+
module Admin
|
126
|
+
class UsersController < ApplicationController
|
127
|
+
end
|
128
|
+
end
|
129
|
+
EOS
|
130
|
+
|
131
|
+
center <<-EOS
|
132
|
+
Constant Name Resolution (2)
|
133
|
+
EOS
|
134
|
+
|
135
|
+
code <<-EOS
|
136
|
+
M::X
|
137
|
+
EOS
|
138
|
+
|
139
|
+
center <<-EOS
|
140
|
+
Constant Name Resolution (3)
|
141
|
+
EOS
|
142
|
+
|
143
|
+
code <<-EOS
|
144
|
+
module M
|
145
|
+
module N
|
146
|
+
class C < D
|
147
|
+
X
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
EOS
|
152
|
+
end
|
153
|
+
|
154
|
+
section "Constant Autoloading" do
|
155
|
+
code <<-EOS
|
156
|
+
module Admin
|
157
|
+
class UsersController < ApplicationController
|
158
|
+
def index
|
159
|
+
@users = User.all
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
163
|
+
EOS
|
164
|
+
|
165
|
+
code <<-EOS
|
166
|
+
# active_support/dependencies.rb
|
167
|
+
|
168
|
+
def self.const_missing(const_name)
|
169
|
+
name # => "Admin::UsersController"
|
170
|
+
const_name # => :User
|
171
|
+
end
|
172
|
+
EOS
|
173
|
+
|
174
|
+
code <<-EOS
|
175
|
+
config.autoload_paths
|
176
|
+
EOS
|
177
|
+
|
178
|
+
block <<-EOS
|
179
|
+
admin/users_controller/user.rb
|
180
|
+
admin/users_controller/user
|
181
|
+
|
182
|
+
# trade-offs
|
183
|
+
|
184
|
+
admin/user.rb
|
185
|
+
admin/user
|
186
|
+
|
187
|
+
# trade-offs
|
188
|
+
|
189
|
+
user.rb # FOUND
|
190
|
+
EOS
|
191
|
+
|
192
|
+
code <<-EOS
|
193
|
+
class Contact < ActiveRecord::Base
|
194
|
+
after_commit :register_event
|
195
|
+
|
196
|
+
def register_event
|
197
|
+
Worker::EventRegister.perform_async(...)
|
198
|
+
end
|
199
|
+
end
|
200
|
+
EOS
|
201
|
+
|
202
|
+
block <<-EOS
|
203
|
+
contact/worker.rb
|
204
|
+
contact/worker
|
205
|
+
|
206
|
+
# trade-offs
|
207
|
+
|
208
|
+
worker.rb
|
209
|
+
worker # FOUND
|
210
|
+
EOS
|
211
|
+
|
212
|
+
code <<-EOS
|
213
|
+
Object.const_set("Worker", Module.new)
|
214
|
+
EOS
|
215
|
+
|
216
|
+
block <<-EOS
|
217
|
+
We keep track of:
|
218
|
+
|
219
|
+
* Fully qualified names of autoloaded constants
|
220
|
+
|
221
|
+
* Their corresponding file names
|
222
|
+
EOS
|
223
|
+
|
224
|
+
center <<-EOS
|
225
|
+
Kernel#load and Kernel#require are wrapped
|
226
|
+
EOS
|
227
|
+
|
228
|
+
# center <<-EOS
|
229
|
+
# require_dependency "sti_grandchildren"
|
230
|
+
# EOS
|
231
|
+
|
232
|
+
block <<-EOS
|
233
|
+
Problem: Which Is The Nesting?
|
234
|
+
EOS
|
235
|
+
|
236
|
+
code <<-EOS
|
237
|
+
# active_support/dependencies.rb
|
238
|
+
|
239
|
+
def self.const_missing(const_name)
|
240
|
+
name # => "Admin::UsersController"
|
241
|
+
const_name # => :User
|
242
|
+
end
|
243
|
+
EOS
|
244
|
+
|
245
|
+
code <<-EOS
|
246
|
+
module M
|
247
|
+
module N
|
248
|
+
# nesting: [M::N, M]
|
249
|
+
end
|
250
|
+
end
|
251
|
+
|
252
|
+
module M::N
|
253
|
+
# nesting: [M::N]
|
254
|
+
end
|
255
|
+
|
256
|
+
module A::B
|
257
|
+
module M::N
|
258
|
+
# nesting: [M::N, A::B]
|
259
|
+
end
|
260
|
+
end
|
261
|
+
EOS
|
262
|
+
|
263
|
+
code <<-EOS1
|
264
|
+
module M
|
265
|
+
Module.new.module_eval <<-EOS
|
266
|
+
# nesting: [#<Module:0x007fa4a284f708>, M]
|
267
|
+
EOS
|
268
|
+
end
|
269
|
+
EOS1
|
270
|
+
|
271
|
+
center <<-EOS
|
272
|
+
Trade-off for Named Modules:
|
273
|
+
EOS
|
274
|
+
|
275
|
+
center <<-EOS
|
276
|
+
The name reflects the nesting
|
277
|
+
EOS
|
278
|
+
|
279
|
+
code <<-EOS
|
280
|
+
module M
|
281
|
+
module N
|
282
|
+
# nesting: [M::N, M]
|
283
|
+
end
|
284
|
+
end
|
285
|
+
EOS
|
286
|
+
|
287
|
+
center <<-EOS
|
288
|
+
Trade-off for Anonymous Modules
|
289
|
+
EOS
|
290
|
+
|
291
|
+
code <<-EOS
|
292
|
+
# active_support/dependencies.rb
|
293
|
+
|
294
|
+
def const_missing(const_name)
|
295
|
+
from_mod = anonymous? ? ::Object : self
|
296
|
+
...
|
297
|
+
end
|
298
|
+
EOS
|
299
|
+
|
300
|
+
block <<-EOS
|
301
|
+
Problem: Which Is The Resolution Algorithm?
|
302
|
+
EOS
|
303
|
+
|
304
|
+
code <<-EOS
|
305
|
+
X = 1
|
306
|
+
|
307
|
+
module M
|
308
|
+
X # => 1
|
309
|
+
end
|
310
|
+
|
311
|
+
M::X # => NameError
|
312
|
+
EOS
|
313
|
+
|
314
|
+
center <<-EOS
|
315
|
+
Trade-off
|
316
|
+
EOS
|
317
|
+
|
318
|
+
code <<-EOS
|
319
|
+
# active_support/dependencies.rb
|
320
|
+
|
321
|
+
from_mod.parents.any? do |p|
|
322
|
+
p.const_defined?(const_name, false)
|
323
|
+
end
|
324
|
+
EOS
|
325
|
+
|
326
|
+
center <<-EOS
|
327
|
+
No attempt is made to follow ancestors
|
328
|
+
EOS
|
329
|
+
|
330
|
+
center <<-EOS
|
331
|
+
Corollary: Active Support does not emulate
|
332
|
+
constant name resolution algorithms
|
333
|
+
EOS
|
334
|
+
end
|
335
|
+
|
336
|
+
section "Request Flow" do
|
337
|
+
code <<-EOS
|
338
|
+
config.cache_classes
|
339
|
+
EOS
|
340
|
+
|
341
|
+
code <<-EOS
|
342
|
+
ActiveSupport::FileUpdateChecker
|
343
|
+
EOS
|
344
|
+
|
345
|
+
code <<-EOS
|
346
|
+
config.autoload_once_paths
|
347
|
+
config.explicitly_unloadable_constants
|
348
|
+
EOS
|
349
|
+
|
350
|
+
code <<-EOS
|
351
|
+
# rails/application.rb
|
352
|
+
|
353
|
+
unless config.cache_classes
|
354
|
+
middleware.use ActionDispatch::Reloader, ...
|
355
|
+
end
|
356
|
+
EOS
|
357
|
+
|
358
|
+
block <<-EOS
|
359
|
+
What is watched and reloaded:
|
360
|
+
|
361
|
+
* Routes
|
362
|
+
|
363
|
+
* Locales
|
364
|
+
|
365
|
+
* Application files:
|
366
|
+
|
367
|
+
- Ruby files under autoload_*
|
368
|
+
|
369
|
+
- db/(schema.rb|structure.sql)
|
370
|
+
EOS
|
371
|
+
|
372
|
+
center <<-EOS
|
373
|
+
If files have changed, autoloaded constants
|
374
|
+
are wiped at the beginning of the request
|
375
|
+
EOS
|
376
|
+
|
377
|
+
code <<-EOS
|
378
|
+
# active_support/dependencies.rb
|
379
|
+
|
380
|
+
autoloaded_constants.each do |const|
|
381
|
+
remove_constant const
|
382
|
+
end
|
383
|
+
|
384
|
+
explicitly_unloadable_constants.each do |const|
|
385
|
+
remove_constant const
|
386
|
+
end
|
387
|
+
EOS
|
388
|
+
|
389
|
+
center <<-EOS
|
390
|
+
Constant access triggers const_missing again
|
391
|
+
because the constants are gone
|
392
|
+
EOS
|
393
|
+
end
|
394
|
+
|
395
|
+
section "That's all, thanks!" do
|
396
|
+
end
|
397
|
+
end
|
data/examples/simple.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'tkn2'
|
2
|
+
|
3
|
+
Tkn2.deck do
|
4
|
+
center "wadus üníçöde"
|
5
|
+
|
6
|
+
code <<-EOS
|
7
|
+
require 'application_controller'
|
8
|
+
require 'post'
|
9
|
+
|
10
|
+
class PostsController < ApplicationController
|
11
|
+
def index
|
12
|
+
@posts = Post.all
|
13
|
+
end
|
14
|
+
end
|
15
|
+
EOS
|
16
|
+
|
17
|
+
section "This is a section" do
|
18
|
+
center <<-EOS
|
19
|
+
Hola
|
20
|
+
Hola
|
21
|
+
EOS
|
22
|
+
end
|
23
|
+
end
|
data/lib/tkn2.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
require "tkn2/version"
|
2
|
+
require "tkn2/slide"
|
3
|
+
require "tkn2/deck"
|
4
|
+
require "tkn2/screen"
|
5
|
+
require "tkn2/ansi_reader"
|
6
|
+
require "tkn2/content_block"
|
7
|
+
require "tkn2/utils"
|
8
|
+
|
9
|
+
module Tkn2
|
10
|
+
def self.deck(&block)
|
11
|
+
Deck.new.tap do |deck|
|
12
|
+
deck.instance_eval(&block)
|
13
|
+
end.present!
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
require 'stringio'
|
2
|
+
|
3
|
+
module Tkn2
|
4
|
+
class ANSIReader
|
5
|
+
SText = 0
|
6
|
+
SCode = 1
|
7
|
+
|
8
|
+
def parse(content)
|
9
|
+
io = StringIO.new(content)
|
10
|
+
state = SText
|
11
|
+
buffer = ''
|
12
|
+
while c = io.getc
|
13
|
+
case state
|
14
|
+
when SCode
|
15
|
+
if c == 'm'
|
16
|
+
code(buffer)
|
17
|
+
state = SText
|
18
|
+
else
|
19
|
+
buffer << c
|
20
|
+
end
|
21
|
+
when SText
|
22
|
+
if c == ?\e
|
23
|
+
state = SCode
|
24
|
+
buffer = ''
|
25
|
+
else
|
26
|
+
char(c)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
class Screen < ANSIReader
|
33
|
+
def initialize(window)
|
34
|
+
@window = window
|
35
|
+
end
|
36
|
+
|
37
|
+
def code(c)
|
38
|
+
c.sub(/\A\[/, '').split(';').each do |n|
|
39
|
+
@window.attrset(MAP[n]) if MAP[n]
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
MAP = {
|
44
|
+
"00" => Curses::A_NORMAL,
|
45
|
+
"01" => Curses::A_BOLD,
|
46
|
+
}
|
47
|
+
|
48
|
+
def char(c)
|
49
|
+
@window.addstr c
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
class Remover < ANSIReader
|
54
|
+
def remove(content)
|
55
|
+
parse(content)
|
56
|
+
string
|
57
|
+
end
|
58
|
+
|
59
|
+
def code(c)
|
60
|
+
end
|
61
|
+
|
62
|
+
def char(c)
|
63
|
+
string << c
|
64
|
+
end
|
65
|
+
|
66
|
+
def string
|
67
|
+
@string ||= ''
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
data/lib/tkn2/deck.rb
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
module Tkn2
|
2
|
+
class Deck
|
3
|
+
def initialize
|
4
|
+
@slides = []
|
5
|
+
@current = 0
|
6
|
+
@renderer = Screen.new
|
7
|
+
end
|
8
|
+
|
9
|
+
def present!
|
10
|
+
@renderer.render(self)
|
11
|
+
end
|
12
|
+
|
13
|
+
def current
|
14
|
+
@slides[@current]
|
15
|
+
end
|
16
|
+
|
17
|
+
def next
|
18
|
+
@current += 1 unless last?
|
19
|
+
end
|
20
|
+
|
21
|
+
def prev
|
22
|
+
@current -= 1 unless first?
|
23
|
+
end
|
24
|
+
|
25
|
+
def first
|
26
|
+
@current = 0
|
27
|
+
end
|
28
|
+
|
29
|
+
def first?
|
30
|
+
@current == 0
|
31
|
+
end
|
32
|
+
|
33
|
+
def last?
|
34
|
+
@current == @slides.size - 1
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def self.slide_method(name, klass)
|
40
|
+
define_method name do |*args|
|
41
|
+
add_slide klass.new(*args)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def add_slide(slide)
|
46
|
+
@slides << slide
|
47
|
+
end
|
48
|
+
|
49
|
+
slide_method :center, Slide::Center
|
50
|
+
slide_method :code, Slide::Code
|
51
|
+
slide_method :block, Slide
|
52
|
+
slide_method :section_header, Slide::SectionHeader
|
53
|
+
|
54
|
+
def section(title, &block)
|
55
|
+
section_header title
|
56
|
+
yield
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
data/lib/tkn2/screen.rb
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'curses'
|
2
|
+
|
3
|
+
module Tkn2
|
4
|
+
class Screen
|
5
|
+
def initialize
|
6
|
+
Curses.init_screen
|
7
|
+
Curses.cbreak
|
8
|
+
Curses.nl
|
9
|
+
Curses.noecho
|
10
|
+
Curses.curs_set 0
|
11
|
+
Curses.stdscr.keypad(true)
|
12
|
+
end
|
13
|
+
|
14
|
+
def render(deck)
|
15
|
+
loop do
|
16
|
+
break unless deck.current
|
17
|
+
|
18
|
+
Curses.clear
|
19
|
+
place_content deck.current.block
|
20
|
+
|
21
|
+
case Curses.getch
|
22
|
+
when 'q'
|
23
|
+
break
|
24
|
+
when 'n', Curses::Key::DOWN, Curses::Key::RIGHT, ' ', Curses::Key::ENTER, Curses::Key::NPAGE
|
25
|
+
deck.next
|
26
|
+
when 'p', Curses::Key::UP, Curses::Key::LEFT, Curses::Key::BACKSPACE, Curses::Key::PPAGE
|
27
|
+
deck.prev
|
28
|
+
when Curses::Key::HOME
|
29
|
+
deck.first
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
Curses.stdscr.close
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def place_content(content)
|
39
|
+
raw_content = ContentBlock.new(ANSIReader::Remover.new.remove(content.content))
|
40
|
+
top = (Curses.lines - raw_content.height) / 2
|
41
|
+
left = (Curses.cols - raw_content.width) / 2
|
42
|
+
window = Curses.stdscr.subwin(raw_content.height, raw_content.width, top, left)
|
43
|
+
ANSIReader::Screen.new(window).parse(content.content)
|
44
|
+
window.refresh
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
end
|
data/lib/tkn2/slide.rb
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
module Tkn2
|
2
|
+
class Slide
|
3
|
+
def initialize(*args)
|
4
|
+
@args = args
|
5
|
+
end
|
6
|
+
|
7
|
+
def block
|
8
|
+
ContentBlock.new(content)
|
9
|
+
end
|
10
|
+
|
11
|
+
def content
|
12
|
+
Utils.strip_heredoc(raw_content)
|
13
|
+
end
|
14
|
+
|
15
|
+
def raw_content
|
16
|
+
@args.first
|
17
|
+
end
|
18
|
+
|
19
|
+
class Center < Slide
|
20
|
+
def content
|
21
|
+
super.lines.map { |l| l.chomp.center(width) }.join("\n")
|
22
|
+
end
|
23
|
+
|
24
|
+
def width
|
25
|
+
ContentBlock.new(Utils.strip_heredoc(raw_content)).width
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
require 'pygments'
|
30
|
+
|
31
|
+
class Code < Slide
|
32
|
+
def lexer
|
33
|
+
@args[1] || 'ruby'
|
34
|
+
end
|
35
|
+
|
36
|
+
def content
|
37
|
+
# Pygments.highlight(super, formatter: 'terminal256', lexer: lexer)
|
38
|
+
Pygments.highlight(super, formatter: 'terminal256', lexer: lexer, options: {style: 'bw'})
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
class SectionHeader < Center
|
43
|
+
def content
|
44
|
+
line = ' ❧❦☙ '.center(width, '–')
|
45
|
+
"#{line}\n\n#{super}\n\n#{line}"
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
data/lib/tkn2/utils.rb
ADDED
data/lib/tkn2/version.rb
ADDED
data/tkn2.gemspec
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'tkn2/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "tkn2"
|
8
|
+
spec.version = Tkn2::VERSION
|
9
|
+
spec.authors = ["Sergio Gil"]
|
10
|
+
spec.email = ["sgilperez@gmail.com"]
|
11
|
+
spec.description = %q{tkn2 is a terminal based presentation tool based on fxn's tkn}
|
12
|
+
spec.summary = %q{tkn2 is a terminal based presentation tool based on fxn's tkn}
|
13
|
+
spec.homepage = "https://github.com/porras/tkn2"
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files`.split($/)
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_dependency "pygments.rb"
|
22
|
+
|
23
|
+
spec.add_development_dependency "bundler", "~> 1.3"
|
24
|
+
spec.add_development_dependency "rake"
|
25
|
+
end
|
metadata
ADDED
@@ -0,0 +1,103 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: tkn2
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Sergio Gil
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2013-12-30 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: pygments.rb
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - '>='
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - '>='
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: bundler
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ~>
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '1.3'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ~>
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '1.3'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rake
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - '>='
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
description: tkn2 is a terminal based presentation tool based on fxn's tkn
|
56
|
+
email:
|
57
|
+
- sgilperez@gmail.com
|
58
|
+
executables: []
|
59
|
+
extensions: []
|
60
|
+
extra_rdoc_files: []
|
61
|
+
files:
|
62
|
+
- .gitignore
|
63
|
+
- .ruby-version
|
64
|
+
- Gemfile
|
65
|
+
- LICENSE.txt
|
66
|
+
- README.md
|
67
|
+
- Rakefile
|
68
|
+
- examples/constant_autoloading_in_ruby_on_rails.rb
|
69
|
+
- examples/simple.rb
|
70
|
+
- lib/tkn2.rb
|
71
|
+
- lib/tkn2/ansi_reader.rb
|
72
|
+
- lib/tkn2/content_block.rb
|
73
|
+
- lib/tkn2/deck.rb
|
74
|
+
- lib/tkn2/screen.rb
|
75
|
+
- lib/tkn2/slide.rb
|
76
|
+
- lib/tkn2/utils.rb
|
77
|
+
- lib/tkn2/version.rb
|
78
|
+
- tkn2.gemspec
|
79
|
+
homepage: https://github.com/porras/tkn2
|
80
|
+
licenses:
|
81
|
+
- MIT
|
82
|
+
metadata: {}
|
83
|
+
post_install_message:
|
84
|
+
rdoc_options: []
|
85
|
+
require_paths:
|
86
|
+
- lib
|
87
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
88
|
+
requirements:
|
89
|
+
- - '>='
|
90
|
+
- !ruby/object:Gem::Version
|
91
|
+
version: '0'
|
92
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - '>='
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
requirements: []
|
98
|
+
rubyforge_project:
|
99
|
+
rubygems_version: 2.0.3
|
100
|
+
signing_key:
|
101
|
+
specification_version: 4
|
102
|
+
summary: tkn2 is a terminal based presentation tool based on fxn's tkn
|
103
|
+
test_files: []
|