Skip to content

Commit

Permalink
Asciidoctor: Ruby style (#618)
Browse files Browse the repository at this point in the history
Enables many of the currently disabled rubocop checks and fixes them. The
biggest change is in the `copy_images` extension which is now split into
two classes, on that processes blocks looking for candidates to copy and
one that does the actual copying. Beyond that I've converted many tests
from blocks into guards. Rubocop seems to really like guards and I find
them pretty readble, partially because they force you to create a new
method in some cases and those methods can have nice names.
  • Loading branch information
nik9000 authored Feb 20, 2019
1 parent 0e19d45 commit ee2d3d2
Show file tree
Hide file tree
Showing 20 changed files with 318 additions and 294 deletions.
27 changes: 0 additions & 27 deletions resources/asciidoctor/.rubocop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -54,33 +54,9 @@ Metrics/MethodLength:
Metrics/PerceivedComplexity:
Enabled: false

Naming/ConstantName:
Enabled: false

Style/BlockDelimiters:
Enabled: false

Style/BracesAroundHashParameters:
Enabled: false

Style/For:
Enabled: false

Style/FrozenStringLiteralComment:
Enabled: false

Style/GuardClause:
Enabled: false

Style/HashSyntax:
Enabled: false

Style/IfUnlessModifier:
Enabled: false

Style/MixinUsage:
Enabled: false

Style/MutableConstant:
Enabled: false

Expand All @@ -95,6 +71,3 @@ Style/StringLiterals:

Style/RedundantReturn:
Enabled: false

Style/RegexpLiteral:
Enabled: false
24 changes: 12 additions & 12 deletions resources/asciidoctor/lib/change_admonition/extension.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
require 'asciidoctor/extensions'
# frozen_string_literal: true

include Asciidoctor
require 'asciidoctor/extensions'

##
# Extensions for marking when something was added, when it *will* be added, or
Expand All @@ -15,21 +15,21 @@
# Foo coming:[6.0.0-beta1]
# Foo deprecated:[6.0.0-beta1]
#
class ChangeAdmonition < Extensions::Group
class ChangeAdmonition < Asciidoctor::Extensions::Group
def activate(registry)
[
[:added, 'added'],
[:coming, 'changed'],
[:deprecated, 'deleted'],
].each { |(name, revisionflag)|
].each do |(name, revisionflag)|
registry.block_macro ChangeAdmonitionBlock.new(revisionflag), name
registry.inline_macro ChangeAdmonitionInline.new(revisionflag), name
}
end
end

##
# Block change admonition.
class ChangeAdmonitionBlock < Extensions::BlockMacroProcessor
class ChangeAdmonitionBlock < Asciidoctor::Extensions::BlockMacroProcessor
use_dsl
name_positional_attributes :version, :passtext

Expand All @@ -43,20 +43,20 @@ def process(parent, _target, attrs)
# We can *almost* go through the standard :admonition conversion but
# that won't render the revisionflag or the revision. So we have to
# go with this funny compound pass thing.
note = Block.new(parent, :pass, :content_model => :compound)
note << Block.new(note, :pass,
note = Asciidoctor::Block.new(parent, :pass, :content_model => :compound)
note << Asciidoctor::Block.new(note, :pass,
:source => "<note revisionflag=\"#{@revisionflag}\" revision=\"#{version}\">",
:attributes => { 'revisionflag' => @revisionflag })
note << Block.new(note, :paragraph,
note << Asciidoctor::Block.new(note, :paragraph,
:source => attrs[:passtext],
:subs => Substitutors::NORMAL_SUBS)
note << Block.new(note, :pass, :source => "</note>")
:subs => Asciidoctor::Substitutors::NORMAL_SUBS)
note << Asciidoctor::Block.new(note, :pass, :source => "</note>")
end
end

##
# Inline change admonition.
class ChangeAdmonitionInline < Extensions::InlineMacroProcessor
class ChangeAdmonitionInline < Asciidoctor::Extensions::InlineMacroProcessor
use_dsl
name_positional_attributes :version, :text
with_format :short
Expand Down
83 changes: 83 additions & 0 deletions resources/asciidoctor/lib/copy_images/copier.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
# frozen_string_literal: true

require 'csv'
require 'fileutils'
require 'set'

module CopyImages
##
# Handles finding images, copying them, and *not* copying them if they have
# already been copied.
class Copier
include Asciidoctor::Logging

def initialize
@copied = Set[]
end

def copy_image(block, uri)
return unless @copied.add? uri # Skip images we've copied before

source = find_source block, uri
return unless source # Skip images we can't find

logger.info message_with_context "copying #{source}", :source_location => block.source_location
copy_image_proc = block.document.attr 'copy_image'
if copy_image_proc
# Delegate to a proc for copying if one is defined. Used for testing.
copy_image_proc.call(uri, source)
else
destination = ::File.join block.document.options[:to_dir], uri
destination_dir = ::File.dirname destination
FileUtils.mkdir_p destination_dir
FileUtils.cp source, destination
end
end

##
# Does a breadth first search starting at the base_dir of the document and
# any referenced resources. This isn't super efficient but it is how a2x works
# and we strive for compatibility.
#
def find_source(block, uri)
to_check = [block.document.base_dir]
checked = []

resources = block.document.attr 'resources'
if resources && !resources.empty?
begin
to_check += CSV.parse_line(resources)
rescue CSV::MalformedCSVError => error
logger.error message_with_context "Error loading [resources]: #{error}",
:source_location => block.source_location
end
end

while (dir = to_check.shift)
checked << block.normalize_system_path(uri, dir)
return checked.last if File.readable? checked.last
next unless Dir.exist?(dir)

Dir.new(dir).each do |f|
next if ['.', '..'].include? f

f = File.join(dir, f)
to_check << f if File.directory?(f)
end
end

# We'll skip images we can't find but we should log something about it so
# we can fix them.
checked.sort! do |lhs, rhs|
by_depth = lhs.scan(%r{/}).count <=> rhs.scan(%r{/}).count
if by_depth != 0
by_depth
else
lhs <=> rhs
end
end
logger.warn message_with_context "can't read image at any of #{checked}", :source_location => block.source_location
nil
end
end
end
190 changes: 72 additions & 118 deletions resources/asciidoctor/lib/copy_images/extension.rb
Original file line number Diff line number Diff line change
@@ -1,138 +1,92 @@
require 'csv'
require 'fileutils'
require 'set'
# frozen_string_literal: true

require_relative '../scaffold.rb'
require_relative 'copier.rb'

include Asciidoctor

##
# Copies images that are referenced into the same directory as the output files.
#
# It finds the images by looking in a comma separated list of directories
# defined by the `resources` attribute.
#
# It can also be configured to copy the images that number callout lists by
# setting `copy-callout-images` to the file extension of the images to copy.
#
class CopyImages < TreeProcessorScaffold
include Logging
ADMONITION_IMAGE_FOR_REVISION_FLAG = {
'added' => 'note',
'changed' => 'note',
'deleted' => 'warning',
}

def initialize(name)
super
@copied = Set[]
end
module CopyImages
##
# Copies images that are referenced into the same directory as the output files.
#
# It finds the images by looking in a comma separated list of directories
# defined by the `resources` attribute.
#
# It can also be configured to copy the images that number callout lists by
# setting `copy-callout-images` to the file extension of the images to copy.
#
# It can also be configured to copy the that decoration admonitions by
# setting `copy-admonition-images` to the file extension of the images
# to copy.
#
class CopyImages < TreeProcessorScaffold
include Asciidoctor::Logging

def process_block(block)
if block.context == :image
uri = block.image_uri(block.attr 'target')
return if Helpers.uriish? uri # Skip external images
ADMONITION_IMAGE_FOR_REVISION_FLAG = {
'added' => 'note',
'changed' => 'note',
'deleted' => 'warning',
}

copy_image block, uri
return
end
callout_extension = block.document.attr 'copy-callout-images'
if callout_extension
if block.parent && block.parent.context == :colist
coids = block.attr('coids')
return unless coids

coids.scan(/CO(?:\d+)-(\d+)/) {
copy_image block, "images/icons/callouts/#{$1}.#{callout_extension}"
}
return
end
def initialize(name)
super
@copier = Copier.new
end
admonition_extension = block.document.attr 'copy-admonition-images'
if admonition_extension
if block.context == :admonition
# The image for a standard admonition comes from the style
style = block.attr 'style'
return unless style

copy_image block, "images/icons/#{style.downcase}.#{admonition_extension}"
return
end
# The image for a change admonition comes from the revisionflag
revisionflag = block.attr 'revisionflag'
if revisionflag
admonition_image = ADMONITION_IMAGE_FOR_REVISION_FLAG[revisionflag]
if admonition_image
copy_image block, "images/icons/#{admonition_image}.#{admonition_extension}"
else
logger.warn message_with_context "unknow revisionflag #{revisionflag}", :source_location => block.source_location
end
return
end

def process_block(block)
process_image block
process_callout block
process_admonition block
end
end

def copy_image(block, uri)
return unless @copied.add? uri # Skip images we've copied before

source = find_source block, uri
return unless source # Skip images we can't find

logger.info message_with_context "copying #{source}", :source_location => block.source_location
copy_image_proc = block.document.attr 'copy_image'
if copy_image_proc
# Delegate to a proc for copying if one is defined. Used for testing.
copy_image_proc.call(uri, source)
else
destination = ::File.join block.document.options[:to_dir], uri
destination_dir = ::File.dirname destination
FileUtils.mkdir_p destination_dir
FileUtils.cp source, destination
def process_image(block)
return unless block.context == :image

uri = block.image_uri(block.attr 'target')
return if Asciidoctor::Helpers.uriish? uri # Skip external images

@copier.copy_image block, uri
end
end

##
# Does a breadth first search starting at the base_dir of the document and
# any referenced resources. This isn't super efficient but it is how a2x works
# and we strive for compatibility.
#
def find_source(block, uri)
to_check = [block.document.base_dir]
checked = []

resources = block.document.attr 'resources'
if resources && !resources.empty?
begin
to_check += CSV.parse_line(resources)
rescue CSV::MalformedCSVError => error
logger.error message_with_context "Error loading [resources]: #{error}",
:source_location => block.source_location
def process_callout(block)
callout_extension = block.document.attr 'copy-callout-images'
return unless callout_extension
return unless block.parent && block.parent.context == :colist

coids = block.attr('coids')
return unless coids

coids.scan(/CO(?:\d+)-(\d+)/) do
@copier.copy_image block, "images/icons/callouts/#{$1}.#{callout_extension}"
end
end

while (dir = to_check.shift)
checked << block.normalize_system_path(uri, dir)
return checked.last if File.readable? checked.last
next unless Dir.exist?(dir)
def process_admonition(block)
admonition_extension = block.document.attr 'copy-admonition-images'
return unless admonition_extension

Dir.new(dir).each { |f|
next if ['.', '..'].include? f
process_standard_admonition admonition_extension, block
process_change_admonition admonition_extension, block
end

def process_standard_admonition(admonition_extension, block)
return unless block.context == :admonition

f = File.join(dir, f)
to_check << f if File.directory?(f)
}
# The image for a standard admonition comes from the style
style = block.attr 'style'
return unless style

@copier.copy_image block, "images/icons/#{style.downcase}.#{admonition_extension}"
end

# We'll skip images we can't find but we should log something about it so
# we can fix them.
checked.sort! { |lhs, rhs|
by_depth = lhs.scan(/\//).count <=> rhs.scan(/\//).count
if by_depth != 0
by_depth
def process_change_admonition(admonition_extension, block)
revisionflag = block.attr 'revisionflag'
return unless revisionflag

admonition_image = ADMONITION_IMAGE_FOR_REVISION_FLAG[revisionflag]
if admonition_image
@copier.copy_image block, "images/icons/#{admonition_image}.#{admonition_extension}"
else
lhs <=> rhs
logger.warn message_with_context "unknow revisionflag #{revisionflag}", :source_location => block.source_location
end
}
logger.warn message_with_context "can't read image at any of #{checked}", :source_location => block.source_location
nil
end
end
end
Loading

0 comments on commit ee2d3d2

Please sign in to comment.