showoff 0.1.4 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.rdoc ADDED
@@ -0,0 +1,280 @@
1
+ = ShowOff Presentation Software
2
+
3
+ ShowOff is a Sinatra web app that reads simple configuration files for a
4
+ presentation. It is sort of like a Keynote web app engine - think S5 +
5
+ Slidedown. I am using it to do all my talks in 2010, because I have a deep
6
+ hatred in my heart for Keynote and yet it is by far the best in the field.
7
+
8
+ The idea is that you setup your markdown slide files in section subdirectories
9
+ and then startup the showoff server in that directory. It will read in your
10
+ showoff.json file for which sections go in which order and then will give
11
+ you a URL to present from.
12
+
13
+ It can:
14
+
15
+ * show simple text
16
+ * show images
17
+ * show syntax highlighted code
18
+ * bullets with incremental advancing
19
+ * re-enact command line interactions
20
+ * call up a menu of sections/slides at any time to jump around
21
+ * execute javascript or ruby live and display results
22
+ * do simple transitions (instant, fade, slide in)
23
+
24
+ It might will can:
25
+
26
+ * show a timer - elapsed / remaining
27
+ * perform simple animations of images moving between keyframes
28
+ * show syncronized, hidden notes on another browser (like an iphone)
29
+ * show audience questions / comments (twitter or direct)
30
+ * let audience members go back / catch up as you talk
31
+ * let audience members vote on sections (?)
32
+ * broadcast itself on Bonjour
33
+ * let audience members download slides, code samples or other supplementary material
34
+
35
+ Some of the nice things are that you can easily version control it, you
36
+ can easily move sections between presentations, and you can rearrange or
37
+ remove sections easily.
38
+
39
+ = Usage
40
+
41
+ ShowOff is meant to be run in a ShowOff formatted repository - that means that it has a showoff.json file and a number of sections (subdirectories) with markdown files for the slides you're presenting.
42
+
43
+ $ gem install showoff
44
+ $ git clone (showoff-repo)
45
+ $ cd (showoff-repo)
46
+ $ showoff serve
47
+
48
+ If you run 'showoff' in the ShowOff directory itself, it will show an example
49
+ presentation from the 'example' subdirectory, so you can see what it's like.
50
+
51
+ = Slide Format
52
+
53
+ You can break your slides up into sections of however many subdirectories deep
54
+ you need. ShowOff will recursively check all the directories mentioned in
55
+ your showoff.json file for any markdown files (.md). Each markdown file can
56
+ have any number of slides in it, seperating each slide with the '!SLIDE'
57
+ keyword and optional slide styles.
58
+
59
+ For example, if you run 'showoff create my_new_pres' it will create a new
60
+ starter presentation for you with one .md file at one/slide.md which will have
61
+ the following contents:
62
+
63
+ !SLIDE
64
+
65
+ # My Presentation #
66
+
67
+ !SLIDE bullets incremental transition=fade
68
+
69
+ # Bullet Points #
70
+
71
+ * first point
72
+ * second point
73
+ * third point
74
+
75
+ That represents two slides, the first contains just a large title, and the
76
+ second is faded into view showing the title and three bullets that are then
77
+ incrementally shown. In order for ShowOff to see those slides, your
78
+ showoff.json file needs to look something like this:
79
+
80
+ [
81
+ {"section":"one"}
82
+ ]
83
+
84
+ If you have multiple sections in your talk, you can make this json array
85
+ include all the sections you want to show in which order you want to show
86
+ them.
87
+
88
+ Some useful styles for each slide are:
89
+
90
+ * center - centers images on a slide
91
+ * full-page - allows an image to take up the whole slide
92
+ * bullets - sizes and seperates bullets properly (fits up to 5, generally)
93
+ * smbullets - sizes and seperates more bullets (smaller, closer together)
94
+ * subsection - creates a different background for titles
95
+ * command - monospaces h1 title slides
96
+ * commandline - for pasted commandline sections (needs leading '$' for commands, then output on subsequent lines)
97
+ * code - monospaces everything on the slide
98
+ * incremental - can be used with 'bullets' and 'commandline' styles, will incrementally update elements on arrow key rather than switch slides
99
+ * small - make all slide text 80%
100
+ * smaller - make all slide text 70%
101
+ * execute - on js highlighted code slides, you can click on the code to execute it and display the results on the slide
102
+
103
+ Check out the example directory included to see examples of most of these.
104
+
105
+ Transitions can be supplied through the use of transition=tname on the !SLIDE
106
+ definition, where tname is one of the following supported transitions:
107
+
108
+ * blindX
109
+ * blindY
110
+ * blindZ
111
+ * cover
112
+ * curtainX
113
+ * curtainY
114
+ * fade
115
+ * fadeZoom
116
+ * growX
117
+ * growY
118
+ * none (this is the default)
119
+ * scrollUp
120
+ * scrollDown
121
+ * scrollLeft
122
+ * scrollRight
123
+ * scrollHorz
124
+ * scrollVert
125
+ * shuffle
126
+ * slideX
127
+ * slideY
128
+ * toss
129
+ * turnUp
130
+ * turnDown
131
+ * turnLeft
132
+ * turnRight
133
+ * uncover
134
+ * wipe
135
+ * zoom
136
+
137
+ The transitions are provided by jQuery Cycle plugin. See http://www.malsup.com/jquery/cycle/browser.html to view the effects and http://www.malsup.com/jquery/cycle/adv2.html for how to add custom effects.
138
+
139
+ You can manage the presentation with the following keys:
140
+
141
+ * space, cursor right: next slide
142
+ * cursor left: previous slide
143
+ * d: debug mode
144
+ * c: table of contents (vi)
145
+ * f: toggle footer
146
+ * z: toggle help
147
+
148
+ = Editor integration
149
+
150
+ The "add slide" feature can allow you to add the necessary boilerplate from your editor. If you are using vim, you can
151
+
152
+ !showoff add -t code Check This Code
153
+
154
+ And your buffer will get
155
+
156
+ !SLIDE
157
+ # Check This Code #
158
+ @@@ Ruby
159
+ code_here()
160
+
161
+ added where your cursor was. Binding this to a keybinding can allow you to add new slides quickly.
162
+
163
+ = Command Line Interface
164
+
165
+ showoff command_name [command-specific options] [--] arguments...
166
+
167
+ * Use the command +help+ to get a summary of commands
168
+ * Use the command <tt>help command_name</tt> to get a help for +command_name+
169
+ * Use <tt>--</tt> to stop command line argument processing; useful if your arguments have dashes in them
170
+
171
+ == Commands
172
+ [<tt>add</tt>] Add a new slide at the end in a given dir
173
+ [<tt>create</tt>] Create new showoff presentation
174
+ [<tt>help</tt>] Shows list of commands or help for one command
175
+ [<tt>heroku</tt>] Setup your presentation to serve on Heroku
176
+ [<tt>serve</tt>] Serves the showoff presentation in the current directory
177
+
178
+ === <tt>add [title]</tt>
179
+
180
+ Add a new slide at the end in a given dir
181
+
182
+ *Aliases*
183
+ * <tt><b>new</b></tt>
184
+
185
+ Outputs or creates a new slide. With -d and -n, a new slide is created in the given dir, numbered to appear as the last slide in that dir (use -u to avoid numbering). Without those, outputs the slide markdown to stdout (useful for shelling out from your editor). You may also specify a source file to use for a code slide
186
+
187
+ ==== Options
188
+ These options are specified *after* the command.
189
+
190
+ [<tt>-d, --dir=dir</tt>] Slide dir (where to put a new slide file)
191
+ [<tt>-n, --name=basename</tt>] Slide name (name of the new slide file)
192
+ [<tt>-s, --source=path to file</tt>] Include code from the given file as the slide body
193
+ [<tt>-t, --style, --type=valid showoff style/type</tt>] Slide Type/Style <i>( default: <tt>title</tt>)</i>
194
+ [<tt>-u, --nonumber</tt>] Dont number the slide, use the given name verbatim
195
+ === <tt>create dir_name</tt>
196
+
197
+ Create new showoff presentation
198
+
199
+ *Aliases*
200
+ * <tt><b>init</b></tt>
201
+
202
+ This command helps start a new showoff presentation by setting up the proper directory structure for you. It takes the directory name you would like showoff to create for you.
203
+
204
+ ==== Options
205
+ These options are specified *after* the command.
206
+
207
+ [<tt>-d, --slidedir=arg</tt>] sample slide directory name <i>( default: <tt>one</tt>)</i>
208
+ [<tt>-n, --nosamples</tt>] Dont create sample slides
209
+ === <tt>help [command]</tt>
210
+
211
+ Shows list of commands or help for one command
212
+
213
+ === <tt>heroku heroku_name</tt>
214
+
215
+ Setup your presentation to serve on Heroku
216
+
217
+ === <tt>serve </tt>
218
+
219
+ Serves the showoff presentation in the current directory
220
+
221
+
222
+
223
+ ==== Options
224
+ These options are specified *after* the command.
225
+
226
+ [<tt>-p, --port=arg</tt>] Port on which to run <i>( default: <tt>9090</tt>)</i>
227
+ = Real World Usage
228
+
229
+ So far, ShowOff has been used in the following presentations:
230
+
231
+ * LinuxConf.au 2010 - Wrangling Git - Scott Chacon
232
+ http://github.com/schacon/showoff-wrangling-git
233
+ * SF Ruby Meetup - Resque! - Chris Wanstrath
234
+ http://github.com/defunkt/sfruby-meetup-resque
235
+ * RORO Sydney Talk, Feb 2010 - Beyond Actions - Dave Bolton
236
+ http://github.com/lightningdb/roro-syd-beyond-actions
237
+ * LRUG's February meeting - Showing Off with Ruby - Joel Chippindale
238
+ http://github.com/mocoso/showing-off-with-ruby
239
+ * PyCon 2010 - Hg and Git; Can't we all just get along? - Scott Chacon
240
+ http://github.com/schacon/pycon-hg-git
241
+ * PdxJs Tech Talk - Asynchronous Coding For My Tiny Ruby Brain - Rick Olson
242
+ http://github.com/technoweenie/pdxjs-twitter-node
243
+ * RORO Perth Talk - Rails 3; A Brief Introduction Darcy Laycock
244
+ http://github.com/Sutto/roro-perth-rails-3
245
+ * PDXRB Tech Talk - Here's Sinatra - Jesse Cooke
246
+ http://github.com/jc00ke/pdxrb_sinatra
247
+
248
+ If you use it for something, please let me know so I can add it.
249
+
250
+ = Future Plans
251
+
252
+ I really want this to evolve into a dynamic presentation software server,
253
+ that gives the audience a lot of interaction into the presentation -
254
+ helping them decide dynamically what the content of the presentation is,
255
+ ask questions without interupting the presenter, etc. I want the audience
256
+ to be able to download a dynamically generated PDF of either the actual
257
+ talk that was given, or all the available slides, plus supplementary
258
+ material. And I want the presenter (me) to be able to push each
259
+ presentation to Heroku or GitHub pages for archiving super easily.
260
+
261
+ = Why Not S5 or Slidy or Slidedown?
262
+
263
+ S5 and Slidy are really cool, and I was going to use them, but mainly I wanted
264
+ something more dynamic. I wanted Slidy + Slidedown, where I could write my
265
+ slideshows in a structured format in sections, where the sections could easily
266
+ be moved around and between presentations and could be written in Markdown. I
267
+ also like the idea of having interactive presentation system and didn't need
268
+ half the features of S5/Slidy (style based print view, auto-scaling, themes,
269
+ etc).
270
+
271
+ = Requirements
272
+
273
+ * Ruby (duh)
274
+ * Sinatra (and thus Rack)
275
+ * BlueCloth
276
+ * Nokogiri
277
+ * json
278
+ * GLI gem
279
+ * Firefox or Chrome to present
280
+
data/bin/showoff CHANGED
@@ -2,19 +2,115 @@
2
2
 
3
3
  $LOAD_PATH.unshift File.join(File.dirname(__FILE__), '..', 'lib')
4
4
  require 'showoff'
5
- command = ARGV.shift
6
-
7
- case command
8
- when 'create'
9
- ShowOffUtils.create
10
- when 'add'
11
- ShowOffUtils.add_slide
12
- when 'heroku'
13
- ShowOffUtils.heroku
14
- when 'serve'
15
- ShowOff.run! :host => 'localhost', :port => 9090
16
- when 'static'
17
- ShowOff.do_static(ARGV)
18
- else
19
- ShowOffUtils.help
5
+ require 'rubygems'
6
+ require 'gli'
7
+
8
+ include GLI
9
+
10
+ desc 'Create new showoff presentation'
11
+ arg_name 'dir_name'
12
+ long_desc 'This command helps start a new showoff presentation by setting up the proper directory structure for you. It takes the directory name you would like showoff to create for you.'
13
+ command [:create,:init] do |c|
14
+
15
+ c.desc 'Don''t create sample slides'
16
+ c.switch [:n,:nosamples]
17
+
18
+ c.desc 'sample slide directory name'
19
+ c.default_value 'one'
20
+ c.flag [:d,:slidedir]
21
+
22
+ c.action do |global_options,options,args|
23
+ raise "dir_name is required" if args.empty?
24
+ ShowOffUtils.create(args[0],!options[:n],options[:d])
25
+ end
26
+ end
27
+
28
+ desc 'Setup your presentation to serve on Heroku'
29
+ arg_name 'heroku_name'
30
+ long_desc 'Creates the .gems file and config.ru file needed to push a showoff pres to heroku. it will then run ''heroku create'' for you to register the new project on heroku and add the remote for you. then all you need to do is commit the new created files and run ''git push heroku'' to deploy.'
31
+ command :heroku do |c|
32
+ c.action do |global_options,options,args|
33
+ raise "heroku_name is required" if args.empty?
34
+ ShowOffUtils.heroku(args[0])
35
+ end
36
+ end
37
+
38
+ desc 'Serves the showoff presentation in the current directory'
39
+ command :serve do |c|
40
+
41
+ c.desc 'Port on which to run'
42
+ c.default_value "9090"
43
+ c.flag [:p,:port]
44
+
45
+ c.desc 'Host or ip to run on'
46
+ c.default_value "localhost"
47
+ c.flag [:h,:host]
48
+
49
+ c.action do |global_options,options,args|
50
+ raise "fail. not a showoff directory" if !File.exists?(ShowOffUtils::SHOWOFF_JSON_FILE)
51
+ ShowOff.run! :host => options[:h], :port => options[:p].to_i
52
+ end
53
+ end
54
+
55
+ desc 'Add a new slide at the end in a given dir'
56
+ arg_name '[title]'
57
+ long_desc 'Outputs or creates a new slide. With -d and -n, a new slide is created in the given dir, numbered to appear as the last slide in that dir (use -u to avoid numbering). Without those, outputs the slide markdown to stdout (useful for shelling out from your editor). You may also specify a source file to use for a code slide'
58
+ command [:add,:new] do |c|
59
+ c.desc 'Don''t number the slide, use the given name verbatim'
60
+ c.switch [:u,:nonumber]
61
+
62
+ c.desc 'Include code from the given file as the slide body'
63
+ c.arg_name 'path to file'
64
+ c.flag [:s,:source]
65
+
66
+ c.desc 'Slide Type/Style'
67
+ c.arg_name 'valid showoff style/type'
68
+ c.default_value 'title'
69
+ c.flag [:t,:type,:style]
70
+
71
+ c.desc 'Slide dir (where to put a new slide file)'
72
+ c.arg_name 'dir'
73
+ c.flag [:d,:dir]
74
+
75
+ c.desc 'Slide name (name of the new slide file)'
76
+ c.arg_name 'basename'
77
+ c.flag [:n,:name]
78
+
79
+ c.action do |global_options,options,args|
80
+ title = args.join(" ")
81
+ ShowOffUtils.add_slide(:dir => options[:d],
82
+ :name => options[:n],
83
+ :title => title,
84
+ :number => !options[:m],
85
+ :code => options[:s],
86
+ :type => options[:t])
87
+ end
88
+ end
89
+
90
+ desc 'Generate static version of presentation'
91
+ arg_name 'name'
92
+ long_desc 'Creates a static, one page version of the presentation as {name}.html'
93
+ command [:static] do |c|
94
+ c.action do |global_options,options,args|
95
+ ShowOff.do_static(args[0])
96
+ end
20
97
  end
98
+
99
+ pre do |global,command,options,args|
100
+ # Pre logic here
101
+ # Return true to proceed; false to abourt and not call the
102
+ # chosen command
103
+ true
104
+ end
105
+
106
+ post do |global,command,options,args|
107
+ # Post logic here
108
+ end
109
+
110
+ on_error do |exception|
111
+ # Error logic here
112
+ # return false to skip default error handling
113
+ true
114
+ end
115
+
116
+ GLI.run(ARGV)
data/lib/showoff.rb CHANGED
@@ -6,6 +6,18 @@ require 'showoff_utils'
6
6
  require 'princely'
7
7
  require 'ftools'
8
8
 
9
+ begin
10
+ require 'RMagick'
11
+ rescue LoadError
12
+ puts 'image sizing disabled - install RMagick'
13
+ end
14
+
15
+ begin
16
+ require 'prawn'
17
+ require 'princely'
18
+ rescue LoadError
19
+ puts 'pdf generation disabled - install prawn'
20
+ end
9
21
 
10
22
  begin
11
23
  require 'rdiscount'
@@ -17,6 +29,8 @@ require 'pp'
17
29
 
18
30
  class ShowOff < Sinatra::Application
19
31
 
32
+ attr_reader :cached_image_size
33
+
20
34
  set :views, File.dirname(__FILE__) + '/../views'
21
35
  set :public, File.dirname(__FILE__) + '/../public'
22
36
  set :pres_dir, 'example'
@@ -31,6 +45,7 @@ class ShowOff < Sinatra::Application
31
45
  options.pres_dir = Dir.pwd
32
46
  @root_path = ".."
33
47
  end
48
+ @cached_image_size = {}
34
49
  puts options.pres_dir
35
50
  @pres_name = options.pres_dir.split('/').pop
36
51
  end
@@ -93,13 +108,33 @@ class ShowOff < Sinatra::Application
93
108
  paths = path.split('/')
94
109
  paths.pop
95
110
  path = paths.join('/')
96
- if static
97
- slide.gsub(/img src=\"(.*?)\"/, 'img src="file://'+options.pres_dir+'/static/' + path + '/\1"')
98
- else
99
- slide.gsub(/img src=\"(.*?)\"/, 'img src="/image/' + path + '/\1"')
111
+ replacement_prefix = static ?
112
+ %(img src="file://#{options.pres_dir}/static/#{path}) :
113
+ %(img src="/image/#{path})
114
+ slide.gsub(/img src=\"(.*?)\"/) do |s|
115
+ img_path = File.join(path, $1)
116
+ w, h = get_image_size(img_path)
117
+ src = %(#{replacement_prefix}/#{$1}")
118
+ if w && h
119
+ src << %( width="#{w}" height="#{h}")
120
+ end
121
+ src
100
122
  end
101
123
  end
102
-
124
+
125
+ if defined?(Magick)
126
+ def get_image_size(path)
127
+ if !cached_image_size.key?(path)
128
+ img = Magick::Image.ping(path).first
129
+ cached_image_size[path] = [img.columns, img.rows]
130
+ end
131
+ cached_image_size[path]
132
+ end
133
+ else
134
+ def get_image_size(path)
135
+ end
136
+ end
137
+
103
138
  def update_commandline_code(slide)
104
139
  html = Nokogiri::XML.parse(slide)
105
140
 
@@ -137,7 +172,7 @@ class ShowOff < Sinatra::Application
137
172
  end
138
173
 
139
174
  def get_slides_html(static=false)
140
- index = File.join(options.pres_dir, 'showoff.json')
175
+ index = File.join(options.pres_dir, ShowOffUtils::SHOWOFF_JSON_FILE )
141
176
  files = []
142
177
  if File.exists?(index)
143
178
  order = JSON.parse(File.read(index))
@@ -214,8 +249,8 @@ class ShowOff < Sinatra::Application
214
249
  end
215
250
 
216
251
 
217
- def self.do_static(args)
218
- what = args.shift || "index"
252
+ def self.do_static(what)
253
+ what = "index" if !what
219
254
 
220
255
  # Nasty hack to get the actual ShowOff module
221
256
  showoff = ShowOff.new
@@ -236,11 +271,12 @@ class ShowOff < Sinatra::Application
236
271
  file.puts(data)
237
272
  file.close
238
273
  # Now copy all the js and css
274
+ my_path = File.join( File.dirname(__FILE__), '..', 'public')
239
275
  ["js", "css"].each { |dir|
240
- FileUtils.copy_entry("#{path}/public/#{dir}", "#{out}/#{dir}")
276
+ FileUtils.copy_entry("#{my_path}/#{dir}", "#{out}/#{dir}")
241
277
  }
242
278
  # And copy the directory
243
- Dir.glob("#{path}/#{name}/*").each { |subpath|
279
+ Dir.glob("#{my_path}/#{name}/*").each { |subpath|
244
280
  base = File.basename(subpath)
245
281
  next if "static" == base
246
282
  next unless File.directory?(subpath) || base.match(/\.(css|js)$/)
@@ -258,14 +294,16 @@ class ShowOff < Sinatra::Application
258
294
  end
259
295
 
260
296
  get %r{/(.*)} do
261
- what = params[:captures].first
262
- what = 'index' if "" == what
263
- data = send(what)
264
- if data.is_a?(File)
265
- send_file data.path
266
- else
267
- data
268
- end
297
+ what = params[:captures].first
298
+ what = 'index' if "" == what
299
+ if (what != "favicon.ico")
300
+ data = send(what)
301
+ if data.is_a?(File)
302
+ send_file data.path
303
+ else
304
+ data
305
+ end
306
+ end
269
307
  end
270
308
 
271
309