fnando-bookmaker 0.0.7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (41) hide show
  1. data/History.txt +41 -0
  2. data/License.txt +20 -0
  3. data/Manifest.txt +38 -0
  4. data/PostInstall.txt +0 -0
  5. data/README.markdown +168 -0
  6. data/Rakefile +4 -0
  7. data/TODO.txt +2 -0
  8. data/app_generators/bookmaker/bookmaker_generator.rb +68 -0
  9. data/app_generators/bookmaker/templates/Rakefile +3 -0
  10. data/app_generators/bookmaker/templates/config.yml +8 -0
  11. data/app_generators/bookmaker/templates/css/active4d.css +114 -0
  12. data/app_generators/bookmaker/templates/css/blackboard.css +88 -0
  13. data/app_generators/bookmaker/templates/css/dawn.css +121 -0
  14. data/app_generators/bookmaker/templates/css/eiffel.css +121 -0
  15. data/app_generators/bookmaker/templates/css/idle.css +62 -0
  16. data/app_generators/bookmaker/templates/css/iplastic.css +80 -0
  17. data/app_generators/bookmaker/templates/css/lazy.css +73 -0
  18. data/app_generators/bookmaker/templates/css/mac_classic.css +123 -0
  19. data/app_generators/bookmaker/templates/css/slush_poppies.css +85 -0
  20. data/app_generators/bookmaker/templates/css/sunburst.css +180 -0
  21. data/app_generators/bookmaker/templates/layouts/boom/layout.css +471 -0
  22. data/app_generators/bookmaker/templates/layouts/boom/layout.html +37 -0
  23. data/app_generators/bookmaker/templates/user.css +0 -0
  24. data/bin/bookmaker +17 -0
  25. data/config/hoe.rb +76 -0
  26. data/config/requirements.rb +15 -0
  27. data/lib/bookmaker.rb +18 -0
  28. data/lib/bookmaker/base.rb +319 -0
  29. data/lib/bookmaker/blackcloth.rb +165 -0
  30. data/lib/bookmaker/tasks.rb +112 -0
  31. data/lib/bookmaker/version.rb +9 -0
  32. data/script/console +10 -0
  33. data/script/destroy +14 -0
  34. data/script/generate +14 -0
  35. data/script/txt2html +82 -0
  36. data/setup.rb +1585 -0
  37. data/test/test_bookmaker.rb +11 -0
  38. data/test/test_bookmaker_generator.rb +43 -0
  39. data/test/test_generator_helper.rb +29 -0
  40. data/test/test_helper.rb +2 -0
  41. metadata +101 -0
@@ -0,0 +1,37 @@
1
+ <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
2
+ "http://www.w3.org/TR/html4/strict.dtd">
3
+ <html>
4
+ <head>
5
+ <meta http-equiv="Content-type" content="text/html; charset=utf-8" />
6
+ <link rel="stylesheet" type="text/css" href="../templates/layout.css"/>
7
+ <link rel="stylesheet" type="text/css" href="../templates/syntax.css"/>
8
+ <link rel="stylesheet" type="text/css" href="../templates/user.css"/>
9
+
10
+ <meta name="author" content="<%%= authors.join(', ') %>" />
11
+ <meta name="subject" content="<%%= subject %>" />
12
+ <meta name="keywords" content="<%%= keywords %>" />
13
+ </head>
14
+ <body>
15
+ <div class="frontcover"></div>
16
+ <div class="halftitlepage">
17
+ <h1 class="no-toc"><%%= title %></h1>
18
+ </div>
19
+
20
+ <div class="titlepage">
21
+ <h1 class="no-toc"><%%= title %></h1>
22
+ <p class="no-toc"><%%= authors.join('<br/>') %></p>
23
+ </div>
24
+
25
+ <div class="imprint">
26
+ <p><%%= copyright %></p>
27
+ </div>
28
+
29
+ <div class="chapters">
30
+ <%%= contents %>
31
+ </div>
32
+
33
+ <div class="white_page"></div>
34
+ <div class="white_page"></div>
35
+ <div class="backcover"></div>
36
+ </body>
37
+ </html>
File without changes
data/bin/bookmaker ADDED
@@ -0,0 +1,17 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rubygems'
4
+ require 'rubigen'
5
+
6
+ if %w(-v --version).include? ARGV.first
7
+ require 'bookmaker/version'
8
+ puts "#{File.basename($0)} #{Bookmaker::VERSION::STRING}"
9
+ exit(0)
10
+ end
11
+
12
+ require 'rubigen/scripts/generate'
13
+ source = RubiGen::PathSource.new(:application,
14
+ File.join(File.dirname(__FILE__), "../app_generators"))
15
+ RubiGen::Base.reset_sources
16
+ RubiGen::Base.append_sources source
17
+ RubiGen::Scripts::Generate.new.run(ARGV, :generator => 'bookmaker')
data/config/hoe.rb ADDED
@@ -0,0 +1,76 @@
1
+ require 'bookmaker/version'
2
+
3
+ AUTHOR = 'Nando Vieira' # can also be an array of Authors
4
+ EMAIL = "fnando.vieira@gmail.com"
5
+ DESCRIPTION = "A framework for creating e-books from Markdown/Textile text markup using Ruby. Using the Prince PDF generator, you'll be able to get high quality PDFs. Mac users that have Textmate installed can have source code highlighted with his favorite theme."
6
+ GEM_NAME = 'bookmaker' # what ppl will type to install your gem
7
+ RUBYFORGE_PROJECT = 'bookmaker' # The unix name for your project
8
+ HOMEPATH = "http://github.com/fnando/bookmaker"
9
+ DOWNLOAD_PATH = "http://github.com/fnando/bookmaker"
10
+ EXTRA_DEPENDENCIES = [
11
+ ['unicode', '>= 0.1'],
12
+ ['RedCloth', '>= 3.0.4'],
13
+ ['rubigen', '>= 1.3.2'],
14
+ ['discount', '>= 1.2.6.1']
15
+ ] # An array of rubygem dependencies [name, version]
16
+
17
+ @config_file = "~/.rubyforge/user-config.yml"
18
+ @config = nil
19
+ RUBYFORGE_USERNAME = "fnando"
20
+ def rubyforge_username
21
+ unless @config
22
+ begin
23
+ @config = YAML.load(File.read(File.expand_path(@config_file)))
24
+ rescue
25
+ puts <<-EOS
26
+ ERROR: No rubyforge config file found: #{@config_file}
27
+ Run 'rubyforge setup' to prepare your env for access to Rubyforge
28
+ - See http://newgem.rubyforge.org/rubyforge.html for more details
29
+ EOS
30
+ exit
31
+ end
32
+ end
33
+ RUBYFORGE_USERNAME.replace @config["username"]
34
+ end
35
+
36
+
37
+ REV = nil
38
+ # UNCOMMENT IF REQUIRED:
39
+ # REV = YAML.load(`svn info`)['Revision']
40
+ VERS = Bookmaker::VERSION::STRING + (REV ? ".#{REV}" : "")
41
+ RDOC_OPTS = ['--quiet', '--title', 'bookmaker documentation',
42
+ "--opname", "index.html",
43
+ "--line-numbers",
44
+ "--main", "README",
45
+ "--inline-source"]
46
+
47
+ class Hoe
48
+ def extra_deps
49
+ @extra_deps.reject! { |x| Array(x).first == 'hoe' }
50
+ @extra_deps
51
+ end
52
+ end
53
+
54
+ # Generate all the Rake tasks
55
+ # Run 'rake -T' to see list of generated tasks (from gem root directory)
56
+ $hoe = Hoe.new(GEM_NAME, VERS) do |p|
57
+ p.developer(AUTHOR, EMAIL)
58
+ p.description = DESCRIPTION
59
+ p.summary = DESCRIPTION
60
+ p.url = HOMEPATH
61
+ p.rubyforge_name = RUBYFORGE_PROJECT if RUBYFORGE_PROJECT
62
+ p.test_globs = ["test/**/test_*.rb"]
63
+ p.clean_globs |= ['**/.*.sw?', '*.gem', '.config', '**/.DS_Store'] #An array of file patterns to delete on clean.
64
+
65
+ # == Optional
66
+ p.changes = p.paragraphs_of("History.txt", 0..1).join("\n\n")
67
+ #p.extra_deps = EXTRA_DEPENDENCIES
68
+
69
+ #p.spec_extras = {} # A hash of extra values to set in the gemspec.
70
+ end
71
+
72
+ CHANGES = $hoe.paragraphs_of('History.txt', 0..1).join("\\n\\n")
73
+ PATH = (RUBYFORGE_PROJECT == GEM_NAME) ? RUBYFORGE_PROJECT : "#{RUBYFORGE_PROJECT}/#{GEM_NAME}"
74
+ $hoe.remote_rdoc_dir = File.join(PATH.gsub(/^#{RUBYFORGE_PROJECT}\/?/,''), 'rdoc')
75
+ $hoe.rsync_args = '-av --delete --ignore-errors'
76
+ $hoe.spec.post_install_message = File.open(File.dirname(__FILE__) + "/../PostInstall.txt").read rescue ""
@@ -0,0 +1,15 @@
1
+ require 'fileutils'
2
+ include FileUtils
3
+
4
+ require 'rubygems'
5
+ %w[rake hoe newgem rubigen].each do |req_gem|
6
+ begin
7
+ require req_gem
8
+ rescue LoadError
9
+ puts "This Rakefile requires the '#{req_gem}' RubyGem."
10
+ puts "Installation: gem install #{req_gem} -y"
11
+ exit
12
+ end
13
+ end
14
+
15
+ $:.unshift(File.join(File.dirname(__FILE__), %w[.. lib]))
data/lib/bookmaker.rb ADDED
@@ -0,0 +1,18 @@
1
+ $:.unshift(File.dirname(__FILE__)) unless
2
+ $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
3
+
4
+ require "rubygems"
5
+ require "yaml"
6
+ require "erb"
7
+ require "ostruct"
8
+ require "rexml/streamlistener"
9
+ require "rexml/document"
10
+ require "hpricot"
11
+
12
+ require "bookmaker/base"
13
+
14
+ begin
15
+ require "ruby-debug"
16
+ rescue LoadError => e
17
+ nil
18
+ end
@@ -0,0 +1,319 @@
1
+ module Bookmaker
2
+ module Markup
3
+ def self.content_for(options)
4
+ source_file = File.join(BOOKMAKER_ROOT, 'code', options[:source_file].to_s)
5
+ code = options[:code]
6
+
7
+ if options[:source_file] && File.exists?(source_file)
8
+ file = File.new(source_file)
9
+
10
+ if options[:from_line] && options[:to_line]
11
+ from_line = options[:from_line].to_i - 1
12
+ to_line = options[:to_line].to_i
13
+ offset = to_line - from_line
14
+ code = file.readlines.slice(from_line, offset).join
15
+ elsif block_name = options[:block_name]
16
+ re = %r(# ?begin: ?#{block_name} ?(?:[^\r\n]+)?\r?\n(.*?)\r?\n([^\r\n]+)?# ?end: #{block_name})sim
17
+ file.read.gsub(re) { |block| code = $1 }
18
+ else
19
+ code = file.read
20
+ code = code.gsub(/&lt;/, '<').gsub(/&gt;/, '>').gsub(/&amp;/, '&')
21
+ end
22
+ end
23
+
24
+ # no code? set to default
25
+ code ||= options[:code]
26
+
27
+ # normalize indentation
28
+ line = StringIO.new(code).readlines[0]
29
+
30
+ if line =~ /^(\t+)/
31
+ char = "\t"
32
+ size = $1.length
33
+ elsif line =~ /^( +)/
34
+ char = " "
35
+ size = $1.length
36
+ end
37
+
38
+ code.gsub! %r(^#{char}{#{size}}), "" if size.to_i > 0
39
+
40
+ # remove all line stubs
41
+ code.gsub! %r(^[\t ]*__$), ""
42
+
43
+ # return
44
+ code
45
+ end
46
+
47
+ def self.syntax(code, syntax='plain_text')
48
+ # get chosen theme
49
+ theme = Bookmaker::Base.config['theme']
50
+ theme = Bookmaker::Base.default_theme unless Bookmaker::Base.theme?(theme)
51
+
52
+ # get syntax
53
+ syntax = Bookmaker::Base.default_syntax unless Bookmaker::Base.syntax?(syntax)
54
+
55
+ Uv.parse(code, "xhtml", syntax, false, theme)
56
+ end
57
+ end
58
+
59
+ module Base
60
+ DEFAULT_LAYOUT = 'boom'
61
+ DEFAULT_THEME = 'eiffel'
62
+ DEFAULT_SYNTAX = 'plain_text'
63
+ GEM_ROOT = File.expand_path(File.dirname(__FILE__) + "/../../")
64
+
65
+ def self.html_path
66
+ BOOKMAKER_ROOT + "/output/#{app_name}.html"
67
+ end
68
+
69
+ def self.pdf_path
70
+ BOOKMAKER_ROOT + "/output/#{app_name}.pdf"
71
+ end
72
+
73
+ def self.template_path
74
+ BOOKMAKER_ROOT + "/templates/layout.html"
75
+ end
76
+
77
+ def self.config_path
78
+ BOOKMAKER_ROOT + "/config.yml"
79
+ end
80
+
81
+ def self.text_dir
82
+ BOOKMAKER_ROOT + "/text"
83
+ end
84
+
85
+ def self.config
86
+ @config ||= YAML::load_file(config_path)
87
+ end
88
+
89
+ def self.parse_layout(contents)
90
+ template = File.new(template_path).read
91
+ contents, toc = self.table_of_contents(contents)
92
+ cfg = config.merge(:contents => contents, :toc => toc)
93
+ env = OpenStruct.new(cfg)
94
+
95
+ ERB.new(template).result env.instance_eval{binding}
96
+ end
97
+
98
+ def self.table_of_contents(contents)
99
+ return [contents, nil] unless Object.const_defined?('Hpricot') && Object.const_defined?('Unicode')
100
+
101
+ doc = Hpricot(contents)
102
+
103
+ (doc/"h2, h3, h4, h5, h6").each do |node|
104
+ title = node.inner_text
105
+ permalink = Bookmaker::Base.to_permalink(title)
106
+ node.set_attribute(:id, permalink)
107
+ end
108
+
109
+ contents = doc.to_html
110
+ io = StringIO.new(contents)
111
+ toc = Toc.new
112
+ REXML::Document.parse_stream(io, toc)
113
+
114
+ [contents, toc.to_s]
115
+ end
116
+
117
+ def self.generate_pdf
118
+ IO.popen('prince %s -o %s' % [html_path, pdf_path])
119
+ end
120
+
121
+ def self.generate_html
122
+ # all parsed markdown file holder
123
+ contents = ""
124
+
125
+ # first, get all chapters; then, get all parsed markdown
126
+ # files from this chapter and group them into a <div class="chapter"> tag
127
+ Dir.entries(text_dir).sort.each do |dirname|
128
+ next if %w(. ..).include?(dirname) || File.file?(text_dir + "/#{dirname}")
129
+
130
+ # gets all parsed markdown files to wrap in a
131
+ # chapter element
132
+ chapter = ""
133
+
134
+ # merge all markdown and textile files into a single list
135
+ markup_files = Dir["#{text_dir}/#{dirname}/*.markdown"] + Dir["#{text_dir}/#{dirname}/*.textile"]
136
+
137
+ # no files, so skip it!
138
+ next if markup_files.empty?
139
+
140
+ markup_files.sort.each do |markup_file|
141
+ # get the file contents
142
+ markup_contents = File.new(markup_file).read
143
+
144
+ # instantiate a markup object
145
+ begin
146
+ if markup_file =~ /\.textile$/
147
+ markup = BlackCloth.new(markup_contents)
148
+ else
149
+ markup = Discount.new(markup_contents)
150
+ end
151
+ rescue Exception => e
152
+ puts "Skipping #{markup_file} (#{e.message})"
153
+ next
154
+ end
155
+
156
+ # convert the markup into html
157
+ parsed_contents = markup.to_html
158
+
159
+ if Object.const_defined?('Uv')
160
+ if markup.respond_to?(:syntax_blocks)
161
+ # textile
162
+ parsed_contents.gsub!(/@syntax:([0-9]+)/m) do |m|
163
+ syntax, code = markup.syntax_blocks[$1.to_i]
164
+ Bookmaker::Markup.syntax(code, syntax)
165
+ end
166
+ else
167
+ # markdown
168
+ parsed_contents.gsub! /<pre><code>(.*?)<\/code><\/pre>/m do |block|
169
+ code = $1.gsub(/&lt;/, '<').gsub(/&gt;/, '>').gsub(/&amp;/, '&')
170
+ code_lines = StringIO.new(code).readlines
171
+ syntax_settings = code_lines.first
172
+
173
+ syntax = 'plain_text'
174
+
175
+ if syntax_settings =~ /syntax\(.*?\)\./
176
+ code = code_lines.slice(1, code_lines.size).join
177
+
178
+ # syntax
179
+ m, syntax = *syntax_settings.match(/syntax\(([^ #]+).*?\)./)
180
+
181
+ # file name
182
+ m, source_file = *syntax_settings.match(/syntax\(.*?\)\. +(.*?)$/)
183
+
184
+ # get line interval
185
+ m, from_line, to_line = *syntax_settings.match(/syntax\(.*? ([0-9]+),([0-9]+)\)/)
186
+
187
+ # get block name
188
+ m, block_name = *syntax_settings.match(/syntax\(.*?#([0-9a-z_]+)\)/)
189
+
190
+ code = Bookmaker::Markup.content_for({
191
+ :code => code,
192
+ :from_line => from_line,
193
+ :to_line => to_line,
194
+ :block_name => block_name,
195
+ :source_file => source_file
196
+ })
197
+
198
+ Bookmaker::Markup.syntax(code, syntax)
199
+ end
200
+ end
201
+ end
202
+ end
203
+
204
+ chapter << (parsed_contents + "\n\n")
205
+ end
206
+
207
+ contents << '<div class="chapter">%s</div>' % chapter
208
+ end
209
+
210
+ # save html file
211
+ File.open(html_path, 'w+') do |f|
212
+ f << Bookmaker::Base.parse_layout(contents)
213
+ end
214
+ end
215
+
216
+ def self.app_name
217
+ ENV['BOOKMAKER_NAME'] || 'bookmaker'
218
+ end
219
+
220
+ def self.theme?(theme_name)
221
+ themes.include?(theme_name)
222
+ end
223
+
224
+ def self.syntax?(syntax_name)
225
+ syntaxes.include?(syntax_name)
226
+ end
227
+
228
+ def self.default_theme
229
+ DEFAULT_THEME
230
+ end
231
+
232
+ def self.default_syntax
233
+ DEFAULT_SYNTAX
234
+ end
235
+
236
+ def self.default_layout
237
+ DEFAULT_LAYOUT
238
+ end
239
+
240
+ def self.syntaxes
241
+ Uv.syntaxes
242
+ end
243
+
244
+ def self.layouts
245
+ @layouts ||= begin
246
+ filter = File.join(GEM_ROOT, "app_generators/bookmaker/templates/layouts/*.css")
247
+ Dir[filter].collect{|path| File.basename(path).gsub(/\.css$/, '') }.sort
248
+ end
249
+ end
250
+
251
+ def self.themes
252
+ @themes ||= begin
253
+ filter = File.join(GEM_ROOT, "app_generators/bookmaker/templates/css/*.css")
254
+ Dir[filter].collect{|path| File.basename(path).gsub(/\.css$/, '') }.sort
255
+ end
256
+ end
257
+
258
+ def self.to_permalink(str)
259
+ str = Unicode.normalize_KD(str).gsub(/[^\x00-\x7F]/n,'')
260
+ str = str.gsub(/[^-_\s\w]/, ' ').downcase.squeeze(' ').tr(' ', '-')
261
+ str = str.gsub(/-+/, '-').gsub(/^-+/, '').gsub(/-+$/, '')
262
+ str
263
+ end
264
+ end
265
+
266
+ class Toc
267
+ include REXML::StreamListener
268
+
269
+ def initialize
270
+ @toc = ""
271
+ @previous_level = 0
272
+ @tag = nil
273
+ @stack = []
274
+ end
275
+
276
+ def header?(tag=nil)
277
+ tag ||= @tag_name
278
+ return false unless tag.to_s =~ /h[2-6]/
279
+ @tag_name = tag
280
+ return true
281
+ end
282
+
283
+ def in_header?
284
+ @in_header
285
+ end
286
+
287
+ def tag_start(name, attrs)
288
+ @tag_name = name
289
+ return unless header?(name)
290
+ @in_header = true
291
+ @current_level = name.gsub!(/[^2-6]/, '').to_i
292
+ @stack << @current_level
293
+ @id = attrs["id"]
294
+
295
+ @toc << %(<ul class="level#{@current_level}">) if @current_level > @previous_level
296
+ @toc << %(</li></ul>) * (@previous_level - @current_level) if @current_level < @previous_level
297
+ @toc << %(</li>) if @current_level <= @previous_level
298
+ @toc << %(<li>)
299
+ end
300
+
301
+ def tag_end(name)
302
+ return unless header?(name)
303
+ @in_header = false
304
+ @previous_level = @current_level
305
+ end
306
+
307
+ def text(str)
308
+ return unless in_header?
309
+ @toc << %(<a href="##{@id}"><span>#{str}</span></a>)
310
+ end
311
+
312
+ def method_missing(*args)
313
+ end
314
+
315
+ def to_s
316
+ @toc + (%(</li></ul>) * (@stack.last - 1))
317
+ end
318
+ end
319
+ end