提交 771a802c 编写于 作者: X Xavier Noria

refactors and fixes in guides generation [ci skip]

This commit is not precisely atomic, but the changes have evolved, summary:

* The ENV-based interface has been moved upwards, the generator has now a conventional
  initializer.

* RAILS_VERSION is now assumed to be a Git tag. A blank RAILS_VERSION means edge guides.

* In consequence, the EDGE env variable is gone.

* The "local" version is also gone, the current SHA1 is computed for edge guides.

* Assumes guides are generated from a repo checkout (time ago users could
  generate them from gems.)

* The WARNINGS flag is gone in consequence, you cannot disable warnings.

* The `api_link` Markdown helper is fixed.

* Docs about usage have one single place: rake guides:help.

* Links in guides have been revised.
上级 4fed08fa
namespace :guides do
desc 'Generate guides (for authors), use ONLY=foo to process just "foo.md"'
task generate: "generate:html"
namespace :generate do
desc "Generate HTML guides"
task :html do
ENV["WARNINGS"] = "1" # authors can't disable this
......@@ -18,7 +16,7 @@ namespace :guides do
abort "Please run `setupkindlerb` to install kindlegen"
end
unless `convert` =~ /convert/
abort "Please install ImageMagick`"
abort "Please install ImageMagick"
end
ENV["KINDLE"] = "1"
Rake::Task["guides:generate:html"].invoke
......@@ -33,7 +31,7 @@ namespace :guides do
desc "Show help"
task :help do
puts <<-help
puts <<HELP
Guides are taken from the source directory, and the result goes into the
output directory. Assets are stored under files, and copied to output/files as
......@@ -46,8 +44,9 @@ All of these processes are handled via rake tasks, here's a full list of them:
#{%x[rake -T]}
Some arguments may be passed via environment variables:
WARNINGS=1
Internal links (anchors) are checked, also detects duplicated IDs.
RAILS_VERSION=tag
If guides are being generated for a specific Rails version set the Git tag
here, otherwise the current SHA1 is going to be used to generate edge guides.
ALL=1
Force generation of all guides.
......@@ -65,15 +64,12 @@ Some arguments may be passed via environment variables:
Use it when you want to generate translated guides in
source/<GUIDES_LANGUAGE> folder (such as source/es)
EDGE=1
Indicate generated guides should be marked as edge.
Examples:
$ rake guides:generate ALL=1
$ rake guides:generate EDGE=1
$ rake guides:generate:kindle EDGE=1
$ rake guides:generate ALL=1 RAILS_VERSION=v5.1.0
$ rake guides:generate ONLY=migrations
$ rake guides:generate:kindle
$ rake guides:generate GUIDES_LANGUAGE=es
help
HELP
end
end
......
pwd = File.dirname(__FILE__)
$:.unshift pwd
$:.unshift __dir__
begin
# Guides generation in the Rails repo.
as_lib = File.join(pwd, "../activesupport/lib")
ap_lib = File.join(pwd, "../actionpack/lib")
as_lib = File.expand_path("../activesupport/lib", __dir__)
ap_lib = File.expand_path("../actionpack/lib", __dir__)
av_lib = File.expand_path("../actionview/lib", __dir__)
$:.unshift as_lib if File.directory?(as_lib)
$:.unshift ap_lib if File.directory?(ap_lib)
rescue LoadError
# Guides generation from gems.
gem "actionpack", ">= 3.0"
end
$:.unshift as_lib if File.directory?(as_lib)
$:.unshift ap_lib if File.directory?(ap_lib)
$:.unshift av_lib if File.directory?(av_lib)
require "rails_guides/generator"
RailsGuides::Generator.new.generate
require "active_support/core_ext/object/blank"
env_value = ->(name) { ENV[name].presence }
env_flag = ->(name) { "1" == env_value[name] }
version = env_value["RAILS_VERSION"]
edge = `git rev-parse HEAD`.strip unless version
RailsGuides::Generator.new(
edge: edge,
version: version,
all: env_flag["ALL"],
only: env_value["ONLY"],
kindle: env_flag["KINDLE"],
language: env_value["GUIDES_LANGUAGE"]
).generate
# ---------------------------------------------------------------------------
#
# This script generates the guides. It can be invoked via the
# guides:generate rake task within the guides directory.
#
# Guides are taken from the source directory, and the resulting HTML goes into the
# output directory. Assets are stored under files, and copied to output/files as
# part of the generation process.
#
# Some arguments may be passed via environment variables:
#
# WARNINGS
# If you are writing a guide, please work always with WARNINGS=1. Users can
# generate the guides, and thus this flag is off by default.
#
# Internal links (anchors) are checked. If a reference is broken levenshtein
# distance is used to suggest an existing one. This is useful since IDs are
# generated by Markdown from headers and thus edits alter them.
#
# Also detects duplicated IDs. They happen if there are headers with the same
# text. Please do resolve them, if any, so guides are valid XHTML.
#
# ALL
# Set to "1" to force the generation of all guides.
#
# ONLY
# Use ONLY if you want to generate only one or a set of guides. Prefixes are
# enough:
#
# # generates only association_basics.html
# ONLY=assoc rake guides:generate
#
# Separate many using commas:
#
# # generates only association_basics.html and command_line.html
# ONLY=assoc,command rake guides:generate
#
# Note that if you are working on a guide generation will by default process
# only that one, so ONLY is rarely used nowadays.
#
# GUIDES_LANGUAGE
# Use GUIDES_LANGUAGE when you want to generate translated guides in
# <tt>source/<GUIDES_LANGUAGE></tt> folder (such as <tt>source/es</tt>).
# Ignore it when generating English guides.
#
# EDGE
# Set to "1" to indicate generated guides should be marked as edge. This
# inserts a badge and changes the preamble of the home page.
#
# ---------------------------------------------------------------------------
require "set"
require "fileutils"
......@@ -64,46 +13,37 @@
module RailsGuides
class Generator
attr_reader :guides_dir, :source_dir, :output_dir, :edge, :warnings, :all
GUIDES_RE = /\.(?:erb|md)\z/
def initialize(output = nil)
set_flags_from_environment
def initialize(edge:, version:, all:, only:, kindle:, language:)
@edge = edge
@version = version
@all = all
@only = only
@kindle = kindle
@language = language
if kindle?
if @kindle
check_for_kindlegen
register_kindle_mime_types
end
initialize_dirs(output)
initialize_dirs
create_output_dir_if_needed
end
def set_flags_from_environment
@edge = ENV["EDGE"] == "1"
@warnings = ENV["WARNINGS"] == "1"
@all = ENV["ALL"] == "1"
@kindle = ENV["KINDLE"] == "1"
@version = ENV["RAILS_VERSION"] || "local"
@lang = ENV["GUIDES_LANGUAGE"]
end
def register_kindle_mime_types
Mime::Type.register_alias("application/xml", :opf, %w(opf))
Mime::Type.register_alias("application/xml", :ncx, %w(ncx))
initialize_markdown_renderer
end
def generate
generate_guides
copy_assets
generate_mobi if kindle?
generate_mobi if @kindle
end
private
def kindle?
@kindle
def register_kindle_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
......@@ -114,29 +54,35 @@ def check_for_kindlegen
def generate_mobi
require "rails_guides/kindle"
out = "#{output_dir}/kindlegen.out"
Kindle.generate(output_dir, mobi, out)
out = "#{@output_dir}/kindlegen.out"
Kindle.generate(@output_dir, mobi, out)
puts "(kindlegen log at #{out})."
end
def mobi
"ruby_on_rails_guides_#@version%s.mobi" % (@lang.present? ? ".#@lang" : "")
mobi = "ruby_on_rails_guides_#{@version || @edge[0, 7]}"
mobi += ".#{@language}" if @language
mobi += ".mobi"
end
def initialize_dirs(output)
@guides_dir = File.join(File.dirname(__FILE__), "..")
@source_dir = "#@guides_dir/source/#@lang"
@output_dir = if output
output
elsif kindle?
"#@guides_dir/output/kindle/#@lang"
else
"#@guides_dir/output/#@lang"
end.sub(%r</$>, "")
def initialize_dirs
@guides_dir = File.expand_path("..", __dir__)
@source_dir = "#{@guides_dir}/source"
@source_dir += "/#{@language}" if @language
@output_dir = "#{@guides_dir}/output"
@output_dir += "/kindle" if @kindle
@source_dir += "/#{@language}" if @language
end
def create_output_dir_if_needed
FileUtils.mkdir_p(output_dir)
FileUtils.mkdir_p(@output_dir)
end
def initialize_markdown_renderer
Markdown::Renderer.edge = @edge
Markdown::Renderer.version = @version
end
def generate_guides
......@@ -147,27 +93,27 @@ def generate_guides
end
def guides_to_generate
guides = Dir.entries(source_dir).grep(GUIDES_RE)
guides = Dir.entries(@source_dir).grep(GUIDES_RE)
if kindle?
Dir.entries("#{source_dir}/kindle").grep(GUIDES_RE).map do |entry|
if @kindle
Dir.entries("#{@source_dir}/kindle").grep(GUIDES_RE).map do |entry|
next if entry == "KINDLE.md"
guides << "kindle/#{entry}"
end
end
ENV.key?("ONLY") ? select_only(guides) : guides
@only ? select_only(guides) : guides
end
def select_only(guides)
prefixes = ENV["ONLY"].split(",").map(&:strip)
prefixes = @only.split(",").map(&:strip)
guides.select do |guide|
guide.start_with?("kindle".freeze, *prefixes)
guide.start_with?("kindle", *prefixes)
end
end
def copy_assets
FileUtils.cp_r(Dir.glob("#{guides_dir}/assets/*"), output_dir)
FileUtils.cp_r(Dir.glob("#{@guides_dir}/assets/*"), @output_dir)
end
def output_file_for(guide)
......@@ -179,22 +125,28 @@ def output_file_for(guide)
end
def output_path_for(output_file)
File.join(output_dir, File.basename(output_file))
File.join(@output_dir, File.basename(output_file))
end
def generate?(source_file, output_file)
fin = File.join(source_dir, source_file)
fin = File.join(@source_dir, source_file)
fout = output_path_for(output_file)
all || !File.exist?(fout) || File.mtime(fout) < File.mtime(fin)
@all || !File.exist?(fout) || File.mtime(fout) < File.mtime(fin)
end
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 = @kindle ? "kindle/layout" : "layout"
File.open(output_path, "w") do |f|
view = ActionView::Base.new(source_dir, edge: @edge, version: @version, mobi: "kindle/#{mobi}", lang: @lang)
view = ActionView::Base.new(
@source_dir,
edge: @edge,
version: @version,
mobi: "kindle/#{mobi}",
language: @language
)
view.extend(Helpers)
if guide =~ /\.(\w+)\.erb$/
......@@ -202,10 +154,15 @@ def generate_guide(guide, output_file)
# Passing a template handler in the template name is deprecated. So pass the file name without the extension.
result = view.render(layout: layout, formats: [$1], file: $`)
else
body = File.read(File.join(source_dir, guide))
result = RailsGuides::Markdown.new(view, layout).render(body)
warn_about_broken_links(result) if @warnings
body = File.read("#{@source_dir}/#{guide}")
result = RailsGuides::Markdown.new(
view: view,
layout: layout,
edge: @edge,
version: @version
).render(body)
warn_about_broken_links(result)
end
f.write(result)
......@@ -231,7 +188,7 @@ def extract_anchors(html)
# Footnotes.
anchors += Set.new(html.scan(/<p\s+class="footnote"\s+id="([^"]+)/).flatten)
anchors += Set.new(html.scan(/<sup\s+class="footnote"\s+id="([^"]+)/).flatten)
return anchors
anchors
end
def check_fragment_identifiers(html, anchors)
......
......@@ -4,12 +4,14 @@
module RailsGuides
class Markdown
def initialize(view, layout)
@view = view
@layout = layout
def initialize(view:, layout:, edge:, version:)
@view = view
@layout = layout
@edge = edge
@version = version
@index_counter = Hash.new(0)
@raw_header = ""
@node_ids = {}
@raw_header = ""
@node_ids = {}
end
def render(body)
......@@ -60,7 +62,8 @@ def engine
autolink: true,
strikethrough: true,
superscript: true,
tables: true)
tables: true
)
end
def extract_raw_header_and_body
......
module RailsGuides
class Markdown
class Renderer < Redcarpet::Render::HTML
def initialize(options = {})
super
end
cattr_accessor :edge, :version
def block_code(code, language)
<<-HTML
......@@ -93,32 +91,30 @@ def convert_notes(body)
end
def github_file_url(file_path)
root, rest = file_path.split("/", 2)
tree = version || edge
case root
when "abstract_controller", "action_controller", "action_dispatch"
path = ["actionpack", "lib", root, rest].join("/")
when "active_support", "active_record", "active_model", "action_view",
"action_cable", "action_mailer", "action_pack", "active_job"
path = [root.sub("_", ""), "lib", root, rest].join("/")
else
path = file_path
end
root = file_path[%r{(.+)/}, 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", version || "master", path].join("/")
end
def version
ENV["RAILS_VERSION"]
"https://github.com/rails/rails/tree/#{tree}/#{path}"
end
def api_link(url)
if version && !url.match(/v\d\.\d\.\d/)
url.insert(url.index(".org") + 4, "/#{version}")
url.sub("http://edgeapi", "http://api") if url.include?("edgeapi")
if url =~ %r{http://api\.rubyonrails\.org/v\d+\.}
url
elsif edge
url.sub("api", "edgeapi")
else
url.sub(/(?<=\.org)/, "/#{version}")
end
url
end
end
end
......
<h2>Ruby on Rails Guides (<%= @edge ? @version[0, 7] : @version %>)</h2>
<h2>Ruby on Rails Guides (<%= @edge ? @edge[0, 7] : @version %>)</h2>
<% if @edge %>
<p>
These are <b>Edge Guides</b>, based on the current <a href="https://github.com/rails/rails/tree/<%= @version %>">master</a> branch.
These are <b>Edge Guides</b>, based on <a href="https://github.com/rails/rails/tree/<%= @edge %>">master@<%= @edge[0, 7] %></a>.
</p>
<p>
If you are looking for the ones for the stable version, please check
......
......@@ -5,7 +5,7 @@
<meta name="cover" content="cover" />
<dc-metadata xmlns:dc="http://purl.org/dc/elements/1.1/">
<dc:title>Ruby on Rails Guides (<%= @version %>)</dc:title>
<dc:title>Ruby on Rails Guides (<%= @version || "master@#{@edge[0, 7]}" %>)</dc:title>
<dc:language>en-us</dc:language>
<dc:creator>Ruby on Rails</dc:creator>
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册