jacktang-hacker-slides 1.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.
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License
2
+
3
+ Copyright (c) 2009 Jack Tang
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.textile ADDED
@@ -0,0 +1,58 @@
1
+ h1. Story About HackerSlides
2
+
3
+ Well, I am one not-well-paid programmer undex linux and far far way from such amazing presentation software -- Apple's keynotes. And I meet some other hackers weekly and exchange the industry news and ideas, of course, by presentation. Meanwhile,I'd like my presentation can be pushed to the git repository so that I might refine some great ideas step by step. I am familiar with S5, but tired to write HTML code. To me, Textile syntax is easy to me. So the project launched...
4
+
5
+ h1. Install It!
6
+
7
+ * Install from source
8
+
9
+ <pre>
10
+ git clone git://github.com/jacktang/hacker-slides.git
11
+ rake install
12
+ </pre>
13
+
14
+ * Install through Gem directory
15
+
16
+ <pre>
17
+ gem sources -a http://gems.github.com
18
+ sudo gem install sudo gem install jacktang-hacker-slides
19
+ </pre>
20
+
21
+ h1. Sample Slides
22
+
23
+ * "HackerSlides":http://jacktang.github.com/hackerslides/sample/
24
+
25
+ h1. Amazing? Use It
26
+
27
+ * Create slides from sketch
28
+
29
+ hacker-slides --sketch sample.textile
30
+
31
+ * Convert Textile to S5 slides
32
+
33
+ hacker-slides sample.textile -o slides -b s5-simple
34
+
35
+ * List all bundles/themes
36
+
37
+ hacker-slides -l
38
+
39
+
40
+ h1. Emacs & HackerSlides
41
+
42
+ * Textile mode for Emacs (todo)
43
+ * Generate slide in Emacs (todo)
44
+
45
+
46
+
47
+ h1. Always Thanks Designers
48
+
49
+ * "S5: iBook-compatible themes":http://www.ibiblio.org/mcmahon/s5-iBook/index.html
50
+ * "Presentations S5":http://www.jesusda.com/projects/presentaciones_s5
51
+ * "Windgazer styles":http://www.windgazer.nl/projects/jsrepository/S5.wgd/
52
+ * "Glossdeck, A New Presentation Theme for S5":http://blog.jm3.net/2007/03/18/glossdeck-a-presentation-theme/
53
+ * "Microformats":http://theryanking.com/presentations/
54
+ * http://freshfoo.com/presentations/PyCon-Italia-Due/slides/
55
+ * http://www.forusers.com/static/html/sample.html
56
+ * http://alice.wu-wien.ac.at:8000/s5/presentation?slideshow=1&style=flower
57
+ * http://atuan.com/s5/
58
+ * http://python.net/~goodger/projects/pycon/2007/idiomatic/presentation.html
data/bin/hacker-slides ADDED
@@ -0,0 +1,137 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rubygems'
4
+ require 'ostruct'
5
+ require 'optparse'
6
+
7
+ $LOAD_PATH.unshift(File.dirname(__FILE__) + '/../lib')
8
+
9
+ require 'highline/import'
10
+ require 'hacker_slides'
11
+
12
+ module HackerSlides
13
+ class Command
14
+
15
+ def run(args)
16
+
17
+ opts = OpenStruct.new
18
+
19
+ opt_parser = OptionParser.new do |cmd|
20
+
21
+ cmd.banner = "Usage: slideshow [options] name"
22
+
23
+ cmd.on( '-o', '--output PATH', 'Output Path' ) do |path|
24
+ opts.output = path
25
+ end
26
+
27
+ cmd.on( "-b", "--bundle NAME", "Template Bundle" ) do |name|
28
+ opts.bundle = name
29
+ end
30
+
31
+ cmd.on( "-s", "--sketch", "Create Slides From Sketch" ) do |sketch|
32
+ opts.sketch = true
33
+ end
34
+
35
+ cmd.on( '-f', '--fetch URI', 'Fetch Bundle' ) do |url|
36
+ opts.fetch_uri = url
37
+ end
38
+
39
+ cmd.on( '-l', '--list', 'List Installed Bundles' ) do
40
+ opts.list = true
41
+ end
42
+
43
+ cmd.on( '-r', '--remote URL', 'Remote Bundle Source' ) do |remote_src|
44
+ opts.remote_src = remote_src
45
+ end
46
+
47
+ cmd.on( '-i', '--install BUNDLE', 'Bundle to Install' ) do |install_bundle|
48
+ opts.install_bundle = install_bundle
49
+ end
50
+
51
+
52
+ # todo: find different letter for debug trace switch (use v for version?)
53
+ cmd.on( "-v", "--verbose", "Show debug trace" ) do
54
+ #logger.datetime_format = "%H:%H:%S"
55
+ #logger.level = Logger::DEBUG
56
+ end
57
+
58
+ cmd.on_tail( "-h", "--help", "Show this message" ) do
59
+ puts "HackerSlides: dead simple slides creator for lazy hackers"
60
+ puts
61
+ puts cmd.help
62
+ puts
63
+ puts "Examples:"
64
+ puts " hacker-slides -S sample.textile"
65
+ puts " hacker-slides -o slides sample.textile"
66
+ puts " hacker-slides -b s5-simple sample.textile"
67
+ puts " hacker-slides -l"
68
+ puts " hacker-slides -s http://example.com/bundles/ -i s5-simple"
69
+ puts
70
+ puts "Further information:"
71
+ puts " http://www.github.com/jacktang/hacker-slides"
72
+ puts
73
+ exit
74
+ end
75
+ end
76
+
77
+ opt_parser.parse!( args )
78
+
79
+ puts "HackerSlides Version: #{VERSION} on Ruby #{RUBY_VERSION} (#{RUBY_RELEASE_DATE}) [#{RUBY_PLATFORM}]"
80
+
81
+ if opts.list
82
+ builtin_bundles, usr_bundles = HackerSlides::Bundle.list_bundles
83
+
84
+ if(not usr_bundles.empty?)
85
+ puts
86
+ puts 'User bundles:'
87
+ usr_bundles.each do |bundle|
88
+ puts " * #{bundle}"
89
+ end
90
+ end
91
+ puts
92
+ puts 'Built-in bundles:'
93
+ builtin_bundles.each do |bundle|
94
+ puts " * #{bundle}"
95
+ end
96
+ puts
97
+ elsif opts.install_bundle
98
+ src = opts.remote_src || './'
99
+ bundle = opts.install_bundle
100
+ puts "install #{bundle.inspect} bundle from #{src}"
101
+ HackerSlides::Bundle.install(src, bundle)
102
+
103
+ elsif opts.sketch
104
+ engine = HackerSlides::SlidesEngine.new
105
+ args.each do |arg|
106
+ file = File.expand_path(arg)
107
+ if(! File.exist?(file) || (File.exist?(file) &&
108
+ agree("This task will overwrite the #{arg} file. Are you sure you want to continue? [yn] ")))
109
+ engine.sketch_slides(arg)
110
+ puts "create #{arg} from sketch"
111
+ puts
112
+ end
113
+ end
114
+ else
115
+ engine = HackerSlides::SlidesEngine.new
116
+ bundle_name = opts.bundle || 's5-simple'
117
+ output_dir = opts.output_dir || 'slides/'
118
+
119
+ args.each do |arg|
120
+ bundle_category = bundle_name.split('-').first
121
+ if(bundle_category == 's5')
122
+ HackerSlides::SlidesEngine.send(:include, HackerSlides::S5SlidesGenerator)
123
+ end
124
+ start_here = engine.create_slides(arg, bundle_name, output_dir)
125
+ if(start_here)
126
+ puts "slides are created"
127
+ puts "please check the link: #{start_here}"
128
+ puts
129
+ end
130
+ end
131
+ end
132
+ end
133
+ end
134
+ end
135
+
136
+
137
+ HackerSlides::Command.new.run(ARGV)
Binary file
@@ -0,0 +1,2 @@
1
+ require 'hacker_slides/bundle'
2
+ require 'hacker_slides/engine'
@@ -0,0 +1,114 @@
1
+ require 'rubygems'
2
+ require 'zlib'
3
+ require 'open-uri'
4
+ require 'fileutils'
5
+
6
+ require 'minitar'
7
+
8
+ module HackerSlides
9
+ class Bundle
10
+ class << self
11
+
12
+ # install remote/local bundle
13
+ def install(src, bundle)
14
+
15
+ bundle = "#{bundle}.bundle" if(File.extname(bundle) != '.bundle')
16
+
17
+ if(src)
18
+ bundle_path = File.join(src, bundle)
19
+ else
20
+ bundle_path = File.expand_path(bundle)
21
+ end
22
+ dest = File.join(usr_bundle_dir, bundle)
23
+ open(bundle_path) do |stream|
24
+ File.open(dest, 'wb') do |f|
25
+ f.write(stream.read)
26
+ end
27
+ end
28
+
29
+ end
30
+
31
+ def list_bundles
32
+ builtin_bundles = []
33
+ usr_bundles = []
34
+
35
+ Dir.glob(File.join(usr_bundle_dir, '*')).each do |bundle|
36
+ bundle_name = File.basename(bundle, '.*')
37
+ usr_bundles << bundle_name
38
+ end
39
+
40
+ Dir.glob(File.join(builtin_bundle_dir, '*')).each do |bundle|
41
+ bundle_name = File.basename(bundle, '.*')
42
+ builtin_bundles << bundle_name
43
+ end
44
+ return builtin_bundles, usr_bundles
45
+ end
46
+
47
+ def builtin_bundle_dir
48
+ return File.expand_path(File.dirname(__FILE__) + '/../../bundles')
49
+ end
50
+
51
+ def usr_bundle_dir
52
+ bundle_dir = File.join(cache_dir, 'bundles')
53
+ unless File.exists?(bundle_dir)
54
+ FileUtils.mkdir(bundle_dir)
55
+ end
56
+ return bundle_dir
57
+ end
58
+
59
+ def lookup_bundle_path(bundle_name)
60
+ bundle = File.join(usr_bundle_dir, "#{bundle_name}.bundle")
61
+ if(File.exists?(bundle))
62
+ return bundle
63
+ else
64
+ bundle = File.join(builtin_bundle_dir, "#{bundle_name}.bundle")
65
+ return bundle if(File.exists?(bundle))
66
+ end
67
+ end
68
+
69
+ def cache_dir
70
+ if(PLATFORM =~ /win32/)
71
+ dir = File.join(ENV['USERPROFILE'], '.hackerslides')
72
+ else
73
+ dir = File.join(File.expand_path("~"), ".hackerslides")
74
+ end
75
+
76
+ unless File.exist?(dir)
77
+ FileUtils.mkdir(dir)
78
+ end
79
+ return dir
80
+ end
81
+
82
+ def extract_files_from_bundle(bundle, files, dest)
83
+ Zlib::GzipReader.open(bundle) do |tgz|
84
+ Archive::Tar::Minitar.unpack(tgz, dest, files)
85
+ end
86
+ end
87
+
88
+ def load_manifest(bundle)
89
+ Zlib::GzipReader.open(bundle) do |tgz|
90
+ Archive::Tar::Minitar::Input.open(tgz) do |stream|
91
+ stream.each do |entry|
92
+ if(entry.name == 'MANIFEST')
93
+ manifest = []
94
+ entry.read.split(/\n/).each_with_index do |line,i|
95
+ case line
96
+ when /^\s*$/
97
+ # skip empty lines
98
+ when /^\s*#.*$/
99
+ # skip comment lines
100
+ else
101
+ manifest << line.strip
102
+ end
103
+ end
104
+ return manifest
105
+ end
106
+ end
107
+ end
108
+ end
109
+ return nil
110
+ end
111
+
112
+ end
113
+ end
114
+ end
@@ -0,0 +1,5 @@
1
+ require 'hacker_slides/presentation'
2
+ require 'hacker_slides/engine/base'
3
+ require 'hacker_slides/engine/slides_engine'
4
+ require 'hacker_slides/engine/markup_engine'
5
+ require 'hacker_slides/engine/s5_slides_generator'
@@ -0,0 +1,6 @@
1
+ module HackerSlides
2
+ module Engine
3
+ class Base
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,36 @@
1
+ require 'rubygems'
2
+ require 'redcloth'
3
+ require 'bluecloth'
4
+
5
+ module HackerSlides
6
+ class MarkupEngine < Engine::Base
7
+
8
+ def to_html(content)
9
+ raise NotImplementedError, 'implmenent the method in subclass'
10
+ end
11
+ end
12
+
13
+ class TextileMarkupEngine < MarkupEngine
14
+ def self.support_extnames
15
+ ['.textile', '.t']
16
+ end
17
+
18
+ def to_html(content)
19
+ # turn off hard line breaks
20
+ # turn off span caps (see http://rubybook.ca/2008/08/16/redcloth)
21
+ red = RedCloth.new(content, [:no_span_caps])
22
+ red.hard_breaks = false
23
+ return red.to_html
24
+ end
25
+ end
26
+
27
+ class MarkdownMarkupEngine < MarkupEngine
28
+ def self.support_extnames
29
+ ['.markdown', '.m', '.mark', '.mkdn', '.md', '.txt', '.text']
30
+ end
31
+
32
+ def to_html(content)
33
+ return BlueCloth.new( content ).to_html
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,12 @@
1
+ module HackerSlides
2
+ module Engine
3
+ class RenderEngine
4
+
5
+ end
6
+
7
+ class ERbRenderEngine < RenderEngine
8
+
9
+ end
10
+ end
11
+ end
12
+
@@ -0,0 +1,33 @@
1
+ module HackerSlides
2
+ module S5SlidesGenerator
3
+
4
+ def post_processing(content)
5
+ # create s5 slides
6
+ slide_counter = 0
7
+ result = ''
8
+
9
+ company = @presentation.meta[:company]
10
+ website = @presentation.meta[:website]
11
+
12
+ preface = "<div class=\"slide\">\n"
13
+ preface << "<h1>#{@presentation.title}</h1>\n"
14
+ preface << "<h3>#{@presentation.author}</h3>\n"
15
+ preface << "<h4><a href=\"#{website}\">#{company}</a></h4>" if(company)
16
+ preface << "</div>"
17
+
18
+ result << preface
19
+
20
+ # wrap h1's in slide divs; note use just <h1 since some processors add ids e.g. <h1 id='x'>
21
+ content.each_line do |line|
22
+ if line.include?( '<h1' ) then
23
+ result << "\n\n</div>" if slide_counter > 0
24
+ result << "<div class='slide'>\n"
25
+ slide_counter += 1
26
+ end
27
+ result << line
28
+ end
29
+ result << "\n</div>" if slide_counter > 0
30
+ return result
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,157 @@
1
+ require 'rubygems'
2
+ require 'erb'
3
+ require 'fileutils'
4
+ require 'highline/import'
5
+
6
+ require 'hacker_slides/bundle'
7
+ require 'hacker_slides/helper'
8
+ require 'hacker_slides/engine/base'
9
+ require 'hacker_slides/engine/markup_engine'
10
+
11
+ module HackerSlides
12
+ class SlidesEngine < Engine::Base
13
+ include HackerSlides::ContentHelper
14
+
15
+ attr_reader :presentation
16
+
17
+ # Create one SlidesEngine instance
18
+ #
19
+ def initialize
20
+ @presentation = HackerSlides::Presentation.new
21
+ end
22
+
23
+
24
+ #
25
+ #
26
+ def create_slides(input, bundle_name, output_dir)
27
+
28
+ dirname = File.dirname(input)
29
+ basename = File.basename(input, '.*')
30
+ extname = File.extname(input)
31
+
32
+ input_file = File.expand_path(input)
33
+ if(not File.exist?(input_file))
34
+ if(agree("Could you like to create the presentation from sketch [yn] "))
35
+ sketch_slides(input_file)
36
+ puts "generate #{input_file} from sketch"
37
+ return
38
+ end
39
+ end
40
+
41
+ output_dir = File.expand_path(File.join(output_dir, basename))
42
+ FileUtils.mkdir_p(output_dir) unless File.directory?(output_dir)
43
+
44
+ bundle_path = HackerSlides::Bundle.lookup_bundle_path(bundle_name)
45
+
46
+ manifest = HackerSlides::Bundle.load_manifest(bundle_path)
47
+
48
+ content_with_metas = File.read(input_file)
49
+
50
+ # read source document; split off optional header from source
51
+ # strip leading optional headers (key/value pairs) including optional empty lines
52
+ read_metas = true
53
+ content = ''
54
+
55
+ content_with_metas.each do |line|
56
+ if read_metas && line =~ /^\s*(\w[\w-]*)[ \t]*:[ \t]*(.*)/
57
+ name = $1.downcase
58
+ value = $2.strip
59
+ if(name == 'title')
60
+ @presentation.title = value
61
+ else
62
+ @presentation.meta[name.to_sym] = value
63
+ end
64
+ elsif line =~ /^\s*$/
65
+ content << line unless read_metas
66
+ else
67
+ read_metas = false
68
+ content << line
69
+ end
70
+ end
71
+
72
+ # ruby note: .*? is non-greedy (shortest-possible) regex match
73
+ content.gsub!(/__SKIP__.*?__END__/m, '')
74
+ content.sub!(/__END__.*/m, '')
75
+
76
+ # you can use the meta
77
+ content = ERB.new(content).result(binding)
78
+
79
+ case markup_type(extname)
80
+ when :textile
81
+ markup_engine = HackerSlides::TextileMarkupEngine.new
82
+ when :markdown
83
+ markup_engine = HackerSlides::MarkdownMarkupEngine.new
84
+ else
85
+ puts "only textile and markdown markup supported"
86
+ puts "#{extname.inspect} doesn't support util now."
87
+ end
88
+
89
+ content = markup_engine.to_html(content) if(markup_engine)
90
+
91
+ # implement in
92
+ slides = post_processing(content) if defined?(:post_processing)
93
+
94
+ start_here = nil
95
+ static_files = []
96
+
97
+ # copy manifest files
98
+ manifest.each do |file|
99
+ if(File.extname(file) == '.erb')
100
+ template_file = File.basename(file)
101
+ if(template_file == 'template.html.erb')
102
+ outname = 'index.html'
103
+ start_here = with_output_path(outname, output_dir)
104
+ else
105
+ outname = File.basename(file, '.*')
106
+ end
107
+ File.open(with_output_path(outname, output_dir), "w+") do |out|
108
+ Zlib::GzipReader.open(bundle_path) do |tgz|
109
+ Archive::Tar::Minitar::Input.open(tgz) do |stream|
110
+ stream.each { |entry| out.puts render_erb_template(entry.read, binding) if(entry.full_name == file )}
111
+ end
112
+ end
113
+ end
114
+ else
115
+ static_files << file
116
+ end
117
+
118
+ HackerSlides::Bundle.extract_files_from_bundle(bundle_path, static_files, output_dir)
119
+ end
120
+
121
+ return start_here
122
+ end
123
+
124
+ def sketch_slides(input_file)
125
+ extname = File.extname(input_file)
126
+ case markup_type(extname)
127
+ when :textile
128
+ sample_file = File.join(File.dirname(__FILE__) + '/../../../samples/sample.textile')
129
+ when :markdown
130
+ sample_file = File.join(File.dirname(__FILE__) + '/../../../samples/sample.markdown')
131
+ else
132
+ puts "only textile and markdown markup supported"
133
+ puts "#{extname.inspect} doesn't support util now."
134
+ end
135
+ FileUtils.cp(sample_file, input_file)
136
+ end
137
+
138
+ def markup_type(extname)
139
+ if(HackerSlides::TextileMarkupEngine.support_extnames.include?(extname))
140
+ return :textile
141
+ end
142
+
143
+ if(HackerSlides::MarkdownMarkupEngine.support_extnames.include?(extname))
144
+ return :markdown
145
+ end
146
+ end
147
+
148
+ def with_output_path(output_file, output_dir)
149
+ return File.expand_path(File.join(output_dir, output_file))
150
+ end
151
+
152
+ def render_erb_template(content, the_binding)
153
+ ERB.new(content).result(the_binding)
154
+ end
155
+
156
+ end
157
+ end