Merge pull request #45539 from geongeorge/feature/updates-guide-generation-epub

Feature/updates guide generation - EPUB
This commit is contained in:
Eileen M. Uchitelle 2022-08-04 15:03:56 -04:00 committed by GitHub
commit 914ac17156
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 409 additions and 257 deletions

View File

@ -51,8 +51,8 @@ group :doc do
gem "sdoc", ">= 2.4.0"
gem "redcarpet", "~> 3.2.3", platforms: :ruby
gem "w3c_validators", "~> 1.3.6"
gem "kindlerb", "~> 1.2.0"
gem "rouge"
gem "rubyzip", "~> 2.0"
end
# Active Support

View File

@ -181,7 +181,7 @@ GEM
crass (1.0.6)
cssbundling-rails (1.1.0)
railties (>= 6.0.0)
curses (1.4.3)
curses (1.4.4)
daemons (1.4.1)
dalli (3.2.0)
dante (0.2.0)
@ -303,9 +303,6 @@ GEM
railties (>= 6.0.0)
json (2.6.1)
jwt (2.3.0)
kindlerb (1.2.0)
mustache
nokogiri
libxml-ruby (3.2.1)
listen (3.7.0)
rb-fsevent (~> 0.10, >= 0.10.3)
@ -336,7 +333,6 @@ GEM
msgpack (1.4.2)
multi_json (1.15.0)
multipart-post (2.1.1)
mustache (1.1.1)
mustermann (1.1.1)
ruby2_keywords (~> 0.0.1)
mysql2 (0.5.4)
@ -583,7 +579,6 @@ DEPENDENCIES
importmap-rails
jsbundling-rails
json (>= 2.0.0)
kindlerb (~> 1.2.0)
libxml-ruby
listen (~> 3.3)
minitest (>= 5.15.0)
@ -614,6 +609,7 @@ DEPENDENCIES
rubocop-packaging
rubocop-performance
rubocop-rails
rubyzip (~> 2.0)
sdoc (>= 2.4.0)
selenium-webdriver (>= 4.0.0)
sequel
@ -637,4 +633,4 @@ DEPENDENCIES
websocket-client-simple!
BUNDLED WITH
2.3.14
2.3.17

View File

@ -10,16 +10,16 @@ namespace :guides do
ruby "-Eutf-8:utf-8", "rails_guides.rb"
end
desc "Generate .mobi file. The kindlegen executable must be in your PATH. You can get it for free from http://www.amazon.com/gp/feature.html?docId=1000765211"
desc "Generate .mobi file"
task :kindle do
require "kindlerb"
unless Kindlerb.kindlegen_available?
abort "Please run `setupkindlerb` to install kindlegen"
require "active_support/deprecation"
ActiveSupport::Deprecation.warn("The guides:generate:kindle rake task is deprecated and will be removed in 7.2. Run rake guides:generate:epub instead")
Rake::Task["guides:generate:epub"].invoke
end
unless /convert/.match?(`convert`)
abort "Please install ImageMagick"
end
ENV["KINDLE"] = "1"
desc "Generate .epub file"
task :epub do
ENV["EPUB"] = "1"
Rake::Task["guides:generate:html"].invoke
end
end
@ -68,7 +68,7 @@ Some arguments may be passed via environment variables:
Examples:
$ rake guides:generate ALL=1 RAILS_VERSION=v5.1.0
$ rake guides:generate ONLY=migrations
$ rake guides:generate:kindle
$ rake guides:generate:epub
$ rake guides:generate GUIDES_LANGUAGE=es
HELP
end

View File

@ -9,3 +9,8 @@ p, H1, H2, H3, H4, H5, H6, H7, H8, table { margin-top: 1em;}
#toc .document {
text-indent: 2em;
}
img {
display: block;
max-width: 100%;
}

View File

@ -24,7 +24,7 @@ RailsGuides::Generator.new(
version: version,
all: env_flag["ALL"],
only: env_value["ONLY"],
kindle: env_flag["KINDLE"],
epub: env_flag["EPUB"],
language: env_value["GUIDES_LANGUAGE"],
direction: env_value["DIRECTION"]
).generate

View File

@ -0,0 +1,94 @@
#!/usr/bin/env ruby
# frozen_string_literal: true
require "nokogiri"
require "fileutils"
require "yaml"
require "date"
require "rails_guides/epub_packer"
module Epub # :nodoc:
extend self
def generate(output_dir, epub_outfile)
fix_file_names(output_dir)
generate_meta_files(output_dir)
generate_epub(output_dir, epub_outfile)
end
private
def open_toc_doc(toc)
Nokogiri::XML(toc).xpath("//ncx:content", "ncx" => "http://www.daisy.org/z3986/2005/ncx/")
end
def generate_meta_files(output_dir)
output_dir = File.absolute_path(File.join(output_dir, ".."))
Dir.chdir output_dir do
puts "=> Using output dir: #{output_dir}"
puts "=> Generating meta files"
FileUtils.mkdir_p("META-INF")
File.write("META-INF/container.xml", <<~CONTENT)
<?xml version="1.0" encoding="UTF-8"?>
<container xmlns="urn:oasis:names:tc:opendocument:xmlns:container" version="1.0">
<rootfiles>
<rootfile full-path="OEBPS/rails_guides.opf" media-type="application/oebps-package+xml"/>
</rootfiles>
</container>
CONTENT
end
end
def generate_epub(output_dir, epub_outfile)
output_dir = File.absolute_path(File.join(output_dir, ".."))
Dir.chdir output_dir do
puts "=> Generating EPUB"
EpubPacker.pack("./", epub_outfile)
puts "=> Done Generating EPUB"
end
end
def is_name_invalid(name)
(/[0-9]/ === name.chars.first)
end
def fix_file_names(output_dir)
book_dir = File.absolute_path(output_dir)
Dir.chdir book_dir do
puts "=> Using book dir: #{book_dir}"
puts "=> Fixing filenames in Table of Contents"
# opf file: item->id and itemref->idref attributes does not support values starting with a number
toc = File.read("toc.ncx")
toc_html = File.read("toc.html")
opf = File.read("rails_guides.opf")
doc = open_toc_doc(toc)
doc.each do |c|
name = c[:src]
if is_name_invalid(name)
FileUtils.mv(name, "rails_#{name}")
toc.gsub!(name, "rails_#{name}")
toc_html.gsub!(name, "rails_#{name}")
opf.gsub!(name, "rails_#{name}")
end
end
File.write("toc.ncx", toc)
File.write("toc.html", toc_html)
File.write("rails_guides.opf", opf)
end
end
def add_head_section(doc, title)
head = Nokogiri::XML::Node.new "head", doc
title_node = Nokogiri::XML::Node.new "title", doc
title_node.content = title
title_node.parent = head
css = Nokogiri::XML::Node.new "link", doc
css["rel"] = "stylesheet"
css["type"] = "text/css"
css["href"] = "#{Dir.pwd}/stylesheets/epub.css"
css.parent = head
doc.at("body").before head
end
end

View File

@ -0,0 +1,59 @@
#!/usr/bin/env ruby
# frozen_string_literal: true
require "nokogiri"
require "fileutils"
require "yaml"
require "date"
require "zip"
module EpubPacker # :nodoc:
extend self
def pack(output_dir, epub_file_name)
@output_dir = output_dir
FileUtils.rm_f(epub_file_name)
Zip::OutputStream.open(epub_file_name) {
|epub|
create_epub(epub, epub_file_name)
}
entries = Dir.entries(output_dir) - %w[. ..]
entries.reject! { |item| File.extname(item) == ".epub" }
Zip::File.open(epub_file_name, create: true) do |epub|
write_entries(entries, "", epub)
end
end
def create_epub(epub, epub_file_name)
epub.put_next_entry("mimetype", nil, nil, Zip::Entry::STORED, Zlib::NO_COMPRESSION)
epub.write "application/epub+zip"
end
def write_entries(entries, path, zipfile)
entries.each do |e|
zipfile_path = path == "" ? e : File.join(path, e)
disk_file_path = File.join(@output_dir, zipfile_path)
if File.directory? disk_file_path
recursively_deflate_directory(disk_file_path, zipfile, zipfile_path)
else
put_into_archive(disk_file_path, zipfile, zipfile_path)
end
end
end
def recursively_deflate_directory(disk_file_path, zipfile, zipfile_path)
zipfile.mkdir zipfile_path
subdir = Dir.entries(disk_file_path) - %w[. ..]
write_entries subdir, zipfile_path, zipfile
end
def put_into_archive(disk_file_path, zipfile, zipfile_path)
zipfile.add(zipfile_path, disk_file_path)
end
end

View File

@ -2,6 +2,8 @@
require "set"
require "fileutils"
require "nokogiri"
require "securerandom"
require "active_support/core_ext/string/output_safety"
require "active_support/core_ext/object/blank"
@ -10,23 +12,23 @@ require "action_view"
require "rails_guides/markdown"
require "rails_guides/helpers"
require "rails_guides/epub"
module RailsGuides
class Generator
GUIDES_RE = /\.(?:erb|md)\z/
def initialize(edge:, version:, all:, only:, kindle:, language:, direction: nil)
def initialize(edge:, version:, all:, only:, epub:, language:, direction: nil)
@edge = edge
@version = version
@all = all
@only = only
@kindle = kindle
@epub = epub
@language = language
@direction = direction || "ltr"
if @kindle
check_for_kindlegen
register_kindle_mime_types
if @epub
register_special_mime_types
end
initialize_dirs
@ -37,32 +39,24 @@ module RailsGuides
def generate
generate_guides
copy_assets
generate_mobi if @kindle
generate_epub if @epub
end
private
def register_kindle_mime_types
def register_special_mime_types
Mime::Type.register_alias("application/xml", :opf, %w(opf))
Mime::Type.register_alias("application/xml", :ncx, %w(ncx))
end
def check_for_kindlegen
if `which kindlegen`.blank?
raise "Can't create a kindle version without `kindlegen`."
end
def generate_epub
Epub.generate(@output_dir, epub_filename)
puts "Epub generated at: output/epub/#{epub_filename}"
end
def generate_mobi
require "rails_guides/kindle"
out = "#{@output_dir}/kindlegen.out"
Kindle.generate(@output_dir, mobi, out)
puts "(kindlegen log at #{out})."
end
def mobi
mobi = +"ruby_on_rails_guides_#{@version || @edge[0, 7]}"
mobi << ".#{@language}" if @language
mobi << ".mobi"
def epub_filename
epub_filename = +"ruby_on_rails_guides_#{@version || @edge[0, 7]}"
epub_filename << ".#{@language}" if @language
epub_filename << ".epub"
end
def initialize_dirs
@ -72,7 +66,7 @@ module RailsGuides
@source_dir += "/#{@language}" if @language
@output_dir = "#{@guides_dir}/output"
@output_dir += "/kindle" if @kindle
@output_dir += "/epub/OEBPS" if @epub
@output_dir += "/#{@language}" if @language
end
@ -95,10 +89,9 @@ module RailsGuides
def guides_to_generate
guides = Dir.entries(@source_dir).grep(GUIDES_RE)
if @kindle
Dir.entries("#{@source_dir}/kindle").grep(GUIDES_RE).map do |entry|
next if entry == "KINDLE.md"
guides << "kindle/#{entry}"
if @epub
Dir.entries("#{@source_dir}/epub").grep(GUIDES_RE).map do |entry|
guides << "epub/#{entry}"
end
end
@ -108,7 +101,7 @@ module RailsGuides
def select_only(guides)
prefixes = @only.split(",").map(&:strip)
guides.select do |guide|
guide.start_with?("kindle", *prefixes)
guide.start_with?("epub", *prefixes)
end
end
@ -137,15 +130,16 @@ module RailsGuides
def generate_guide(guide, output_file)
output_path = output_path_for(output_file)
puts "Generating #{guide} as #{output_file}"
layout = @kindle ? "kindle/layout" : "layout"
layout = @epub ? "epub/layout" : "layout"
view = ActionView::Base.with_empty_template_cache.with_view_paths(
[@source_dir],
edge: @edge,
version: @version,
mobi: "kindle/#{mobi}",
epub: "epub/#{epub_filename}",
language: @language,
direction: @direction,
uuid: SecureRandom.uuid
)
view.extend(Helpers)
@ -161,7 +155,8 @@ module RailsGuides
view: view,
layout: layout,
edge: @edge,
version: @version
version: @version,
epub: @epub
).render(body)
warn_about_broken_links(result)

View File

@ -28,6 +28,15 @@ module RailsGuides
documents.reject { |document| document["work_in_progress"] }
end
def all_images
base_path = File.expand_path("../assets", __dir__)
images_path = File.join(base_path, "images/**/*")
@all_images = Dir.glob(images_path).reject { |f| File.directory?(f) }.map { |item|
item.delete_prefix "#{base_path}/"
}
@all_images
end
def docs_for_menu(position = nil)
if position.nil?
documents_by_section

View File

@ -1,116 +0,0 @@
#!/usr/bin/env ruby
# frozen_string_literal: true
require "kindlerb"
require "nokogiri"
require "fileutils"
require "yaml"
require "date"
module Kindle
extend self
def generate(output_dir, mobi_outfile, logfile)
output_dir = File.absolute_path(output_dir)
Dir.chdir output_dir do
puts "=> Using output dir: #{output_dir}"
puts "=> Arranging html pages in document order"
toc = File.read("toc.ncx")
doc = Nokogiri::XML(toc).xpath("//ncx:content", "ncx" => "http://www.daisy.org/z3986/2005/ncx/")
html_pages = doc.filter_map { |c| c[:src] }.uniq
generate_front_matter(html_pages)
generate_sections(html_pages)
generate_document_metadata(mobi_outfile)
puts "Creating MOBI document with kindlegen. This may take a while."
if Kindlerb.run(output_dir)
puts "MOBI document generated at #{File.expand_path(mobi_outfile, output_dir)}"
end
end
end
def generate_front_matter(html_pages)
frontmatter = []
html_pages.delete_if { |x|
if /(toc|welcome|copyright).html/.match?(x)
frontmatter << x unless /toc/.match?(x)
true
end
}
html = frontmatter.map { |x|
Nokogiri::HTML(File.open(x)).at("body").inner_html
}.join("\n")
fdoc = Nokogiri::HTML(html)
fdoc.search("h3").each do |h3|
h3.name = "h4"
end
fdoc.search("h2").each do |h2|
h2.name = "h3"
h2["id"] = h2.inner_text.gsub(/\s/, "-")
end
add_head_section fdoc, "Front Matter"
File.open("frontmatter.html", "w") { |f| f.puts fdoc.to_html }
html_pages.unshift "frontmatter.html"
end
def generate_sections(html_pages)
FileUtils.rm_rf("sections/")
html_pages.each_with_index do |page, section_idx|
FileUtils.mkdir_p("sections/%03d" % section_idx)
doc = Nokogiri::HTML(File.open(page))
title = doc.at("title").inner_text.gsub("Ruby on Rails Guides: ", "")
title = page.capitalize.gsub(".html", "") if title.strip == ""
File.open("sections/%03d/_section.txt" % section_idx, "w") { |f| f.puts title }
doc.xpath("//h3[@id]").each_with_index do |h3, item_idx|
subsection = h3.inner_text
content = h3.xpath("./following-sibling::*").take_while { |x| x.name != "h3" }.map(&:to_html)
item = Nokogiri::HTML(h3.to_html + content.join("\n"))
item_path = "sections/%03d/%03d.html" % [section_idx, item_idx]
add_head_section(item, subsection)
item.search("img").each do |img|
img["src"] = "#{Dir.pwd}/#{img['src']}"
end
item.xpath("//li/p").each { |p| p.swap(p.children); p.remove }
File.open(item_path, "w") { |f| f.puts item.to_html }
end
end
end
def generate_document_metadata(mobi_outfile)
puts "=> Generating _document.yml"
x = Nokogiri::XML(File.open("rails_guides.opf")).remove_namespaces!
cover_jpg = "#{Dir.pwd}/images/rails_guides_kindle_cover.jpg"
cover_gif = cover_jpg.sub(/jpg$/, "gif")
puts `convert #{cover_jpg} #{cover_gif}`
document = {
"doc_uuid" => x.at("package")["unique-identifier"],
"title" => x.at("title").inner_text.gsub(/\(.*$/, " v2"),
"publisher" => x.at("publisher").inner_text,
"author" => x.at("creator").inner_text,
"subject" => x.at("subject").inner_text,
"date" => x.at("date").inner_text,
"cover" => cover_gif,
"masthead" => nil,
"mobi_outfile" => mobi_outfile
}
puts document.to_yaml
File.open("_document.yml", "w") { |f| f.puts document.to_yaml }
end
def add_head_section(doc, title)
head = Nokogiri::XML::Node.new "head", doc
title_node = Nokogiri::XML::Node.new "title", doc
title_node.content = title
title_node.parent = head
css = Nokogiri::XML::Node.new "link", doc
css["rel"] = "stylesheet"
css["type"] = "text/css"
css["href"] = "#{Dir.pwd}/stylesheets/kindle.css"
css.parent = head
doc.at("body").before head
end
end

View File

@ -3,11 +3,12 @@
require "redcarpet"
require "nokogiri"
require "rails_guides/markdown/renderer"
require "rails_guides/markdown/epub_renderer"
require "rails-html-sanitizer"
module RailsGuides
class Markdown
def initialize(view:, layout:, edge:, version:)
def initialize(view:, layout:, edge:, version:, epub:)
@view = view
@layout = layout
@edge = edge
@ -15,6 +16,7 @@ module RailsGuides
@index_counter = Hash.new(0)
@raw_header = ""
@node_ids = {}
@epub = epub
end
def render(body)
@ -59,7 +61,8 @@ module RailsGuides
end
def engine
@engine ||= Redcarpet::Markdown.new(Renderer,
renderer = @epub ? EpubRenderer : Renderer
@engine ||= Redcarpet::Markdown.new(renderer,
no_intra_emphasis: true,
fenced_code_blocks: true,
autolink: true,
@ -91,7 +94,7 @@ module RailsGuides
def generate_structure
@headings_for_index = []
if @body.present?
@body = Nokogiri::HTML.fragment(@body).tap do |doc|
document = Nokogiri::HTML.fragment(@body).tap do |doc|
hierarchy = []
doc.children.each do |node|
@ -117,7 +120,8 @@ module RailsGuides
doc.css("h3, h4, h5, h6").each do |node|
node.inner_html = "<a class='anchorlink' href='##{node[:id]}'>#{node.inner_html}</a>"
end
end.to_html
end
@body = @epub ? document.to_xhtml : document.to_html
end
end

View File

@ -0,0 +1,113 @@
# frozen_string_literal: true
require "rouge"
# Add more common shell commands
Rouge::Lexers::Shell::BUILTINS << "|bin/rails|brew|bundle|gem|git|node|rails|rake|ruby|sqlite3|yarn"
module RailsGuides
class Markdown
class EpubRenderer < Redcarpet::Render::XHTML # :nodoc:
cattr_accessor :edge, :version
def linebreak
"<br/>"
end
def link(url, title, content)
if %r{https?://api\.rubyonrails\.org}.match?(url)
%(<a href="#{api_link(url)}">#{content}</a>)
elsif title
%(<a href="#{url}" title="#{title}">#{content}</a>)
else
%(<a href="#{url}">#{content}</a>)
end
end
def header(text, header_level)
# Always increase the heading level by 1, so we can use h1, h2 heading in the document
header_level += 1
header_with_id = text.scan(/(.*){#(.*)}/)
unless header_with_id.empty?
%(<h#{header_level} id="#{header_with_id[0][1].strip}">#{header_with_id[0][0].strip}</h#{header_level}>)
else
%(<h#{header_level}>#{text}</h#{header_level}>)
end
end
def paragraph(text)
if text =~ %r{^NOTE:\s+Defined\s+in\s+<code>(.*?)</code>\.?$}
%(<div class="note"><p>Defined in <code><a href="#{github_file_url($1)}">#{$1}</a></code>.</p></div>)
elsif /^(TIP|IMPORTANT|CAUTION|WARNING|NOTE|INFO|TODO)[.:]/.match?(text)
convert_notes(text)
elsif text.include?("DO NOT READ THIS FILE ON GITHUB")
elsif text =~ /^\[<sup>(\d+)\]:<\/sup> (.+)$/
linkback = %(<a href="#footnote-#{$1}-ref"><sup>#{$1}</sup></a>)
%(<p class="footnote" id="footnote-#{$1}">#{linkback} #{$2}</p>)
else
text = convert_footnotes(text)
"<p>#{text}</p>"
end
end
private
def convert_footnotes(text)
text.gsub(/\[<sup>(\d+)\]<\/sup>/i) do
%(<sup class="footnote" id="footnote-#{$1}-ref">) +
%(<a href="#footnote-#{$1}">#{$1}</a></sup>)
end
end
def convert_notes(body)
# The following regexp detects special labels followed by a
# paragraph, perhaps at the end of the document.
#
# It is important that we do not eat more than one newline
# because formatting may be wrong otherwise. For example,
# if a bulleted list follows, the first item is not rendered
# as a list item, but as a paragraph starting with a plain
# asterisk.
body.gsub(/^(TIP|IMPORTANT|CAUTION|WARNING|NOTE|INFO|TODO)[.:](.*?)(\n(?=\n)|\Z)/m) do
css_class = \
case $1
when "CAUTION", "IMPORTANT"
"warning"
when "TIP"
"info"
else
$1.downcase
end
%(<div class="#{css_class}"><p>#{$2.strip}</p></div>)
end
end
def github_file_url(file_path)
tree = version || edge
root = file_path[%r{(\w+)/}, 1]
path = \
case root
when "abstract_controller", "action_controller", "action_dispatch"
"actionpack/lib/#{file_path}"
when /\A(action|active)_/
"#{root.sub("_", "")}/lib/#{file_path}"
else
file_path
end
"https://github.com/rails/rails/tree/#{tree}/#{path}"
end
def api_link(url)
if %r{https?://api\.rubyonrails\.org/v\d+\.}.match?(url)
url
elsif edge
url.sub("api", "edgeapi")
else
url.sub(/(?<=\.org)/, "/#{version}")
end
end
end
end
end

View File

@ -7,7 +7,7 @@ Rouge::Lexers::Shell::BUILTINS << "|bin/rails|brew|bundle|gem|git|node|rails|rak
module RailsGuides
class Markdown
class Renderer < Redcarpet::Render::HTML
class Renderer < Redcarpet::Render::HTML # :nodoc:
cattr_accessor :edge, :version
def block_code(code, language)

View File

@ -1,14 +1,9 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"></meta>
<link rel="stylesheet" type="text/css" href="stylesheets/epub.css"></link>
<title><%= yield(:page_title) || 'Ruby on Rails Guides' %></title>
<link rel="stylesheet" type="text/css" href="stylesheets/kindle.css" />
</head>
<body class="guide">

View File

@ -0,0 +1,48 @@
<?xml version="1.0" encoding="UTF-8"?>
<package xmlns="http://www.idpf.org/2007/opf" version="2.0" unique-identifier="RailsGuides">
<metadata xmlns:opf="http://www.idpf.org/2007/opf" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:dcterms="http://purl.org/dc/terms/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<dc:identifier id="RailsGuides" opf:scheme="uuid"><%= @uuid %></dc:identifier>
<dc:title>Ruby on Rails Guides (<%= @version || "main@#{@edge[0, 7]}" %>)</dc:title>
<dc:language>en</dc:language>
<dc:creator>Ruby on Rails</dc:creator>
<dc:publisher>Ruby on Rails</dc:publisher>
<dc:subject>Reference</dc:subject>
<dc:date><%= Time.now.strftime('%Y-%m-%dT%H:%M:%SZ') %></dc:date>
<dc:description>These guides are designed to make you immediately productive with Rails, and to help you understand how all of the pieces fit together.</dc:description>
<meta name="cover" content="cover"/>
</metadata>
<manifest>
<!-- HTML content files [mandatory] -->
<% documents_flat.each do |document| %>
<item id="<%= document['url'] %>" media-type="application/xhtml+xml" href="<%= document['url'] %>" />
<% end %>
<% %w{toc.html welcome.html copyright.html}.each do |url| %>
<item id="<%= url %>" media-type="application/xhtml+xml" href="<%= url %>" />
<% end %>
<item id="toc" media-type="application/x-dtbncx+xml" href="toc.ncx"/>
<item id="cover" media-type="image/jpeg" href="images/rails_guides_kindle_cover.jpg"/>
<item id="stylesheet" href="stylesheets/epub.css" media-type="text/css"/>
<!-- Images -->
<% all_images.each do |image| %>
<item id="<%= image %>" media-type="image/<%= image.split('.').last %>" href="<%= image %>" />
<% end %>
</manifest>
<spine toc="toc">
<itemref idref="toc.html" />
<itemref idref="welcome.html" />
<itemref idref="copyright.html" />
<% documents_flat.each do |document| %>
<itemref idref="<%= document['url'] %>" />
<% end %>
</spine>
<guide>
<reference type="toc" title="Table of Contents" href="toc.html"></reference>
</guide>
</package>

View File

@ -0,0 +1,24 @@
<% content_for :page_title do %>
Ruby on Rails Guides
<% end %>
<h1>Table of Contents</h1>
<div id="toc">
<ul><li><a href="welcome.html">Welcome</a></li></ul>
<% documents_by_section.each_with_index do |section, i| %>
<h3><%= "#{i + 1}." %> <%= section['name'] %></h3>
<ul>
<% section['documents'].each do |document| %>
<li>
<a href="<%= document['url'] %>"><%= document['name'] %></a>
<% if document['work_in_progress']%>(WIP)<% end %>
</li>
<% end %>
</ul>
<% end %>
<hr />
<ul>
<li><a href="copyright.html">Copyright &amp; License</a></li>
</ul>
</div>

View File

@ -18,7 +18,7 @@
</navLabel>
<content src="toc.html"/>
<navPoint class="section" id="welcome" playOrder="1">
<navPoint class="section" id="start" playOrder="1">
<navLabel>
<text>Introduction</text>
</navLabel>
@ -30,7 +30,7 @@
</navLabel>
<content src="welcome.html"/>
</navPoint>
<navPoint class="article" id="copyright" playOrder="4">
<navPoint class="article" id="copyright" playOrder="3">
<navLabel><text>Copyright &amp; License</text></navLabel>
<content src="copyright.html"/>
</navPoint>

View File

@ -10,7 +10,7 @@
<dl>
<dt></dt>
<% unless @edge -%>
<dd class="kindle">Rails Guides are also available for <%= link_to 'Kindle', @mobi %>.</dd>
<dd class="kindle">Rails Guides are also available for <%= link_to 'Kindle', @epub %>.</dd>
<% end -%>
<dd class="work-in-progress">Guides marked with this icon are currently being worked on and will not be available in the Guides Index menu. While still useful, they may contain incomplete information and even errors. You can help by reviewing them and posting your comments and corrections.</dd>
</dl>

View File

@ -1,51 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<package xmlns="http://www.idpf.org/2007/opf" version="2.0" unique-identifier="RailsGuides">
<metadata>
<meta name="cover" content="cover" />
<dc-metadata xmlns:dc="http://purl.org/dc/elements/1.1/">
<dc:title>Ruby on Rails Guides (<%= @version || "main@#{@edge[0, 7]}" %>)</dc:title>
<dc:language>en-us</dc:language>
<dc:creator>Ruby on Rails</dc:creator>
<dc:publisher>Ruby on Rails</dc:publisher>
<dc:subject>Reference</dc:subject>
<dc:date><%= Time.now.strftime('%Y-%m-%d') %></dc:date>
<dc:description>These guides are designed to make you immediately productive with Rails, and to help you understand how all of the pieces fit together.</dc:description>
</dc-metadata>
<x-metadata>
<output content-type="application/x-mobipocket-subscription-magazine" encoding="utf-8"/>
</x-metadata>
</metadata>
<manifest>
<!-- HTML content files [mandatory] -->
<% documents_flat.each do |document| %>
<item id="<%= document['url'] %>" media-type="text/html" href="<%= document['url'] %>" />
<% end %>
<% %w{toc.html welcome.html copyright.html}.each do |url| %>
<item id="<%= url %>" media-type="text/html" href="<%= url %>" />
<% end %>
<item id="toc" media-type="application/x-dtbncx+xml" href="toc.ncx" />
<item id="cover" media-type="image/jpeg" href="images/rails_guides_kindle_cover.jpg"/>
</manifest>
<spine toc="toc">
<itemref idref="toc.html" />
<itemref idref="welcome.html" />
<itemref idref="copyright.html" />
<% documents_flat.each do |document| %>
<itemref idref="<%= document['url'] %>" />
<% end %>
</spine>
<guide>
<reference type="toc" title="Table of Contents" href="toc.html"></reference>
</guide>
</package>

View File

@ -1,23 +0,0 @@
<% content_for :page_title do %>
Ruby on Rails Guides
<% end %>
<h1>Table of Contents</h1>
<div id="toc">
<ul><li><a href="welcome.html">Welcome</a></li></ul>
<% documents_by_section.each_with_index do |section, i| %>
<h3><%= "#{i + 1}." %> <%= section['name'] %></h3>
<ul>
<% section['documents'].each do |document| %>
<li>
<a href="<%= document['url'] %>"><%= document['name'] %></a>
<% if document['work_in_progress']%>(WIP)<% end %>
</li>
<% end %>
</ul>
<% end %>
<hr />
<ul>
<li><a href="copyright.html">Copyright &amp; License</a></li>
</ul>
</div>