Compare commits

...

3 commits

Author SHA1 Message Date
3af946467a
Fix RuboCop offenses 2024-10-18 23:50:26 +04:00
e68be90066
Upgrade Ruby from 3.2.3 to 3.3.5 2024-10-18 23:20:34 +04:00
1421fedd7c
Add references 2024-10-18 23:01:18 +04:00
19 changed files with 171 additions and 99 deletions

View file

@ -5,7 +5,7 @@ require:
- rubocop-rake - rubocop-rake
AllCops: AllCops:
TargetRubyVersion: 3.2 TargetRubyVersion: 3.3
DisplayCopNames: true DisplayCopNames: true
NewCops: enable NewCops: enable
@ -21,6 +21,9 @@ Lint/AmbiguousOperatorPrecedence:
Lint/ReturnInVoidContext: Lint/ReturnInVoidContext:
Enabled: false Enabled: false
Metrics/ClassLength:
Max: 200
Style/AndOr: Style/AndOr:
EnforcedStyle: conditionals EnforcedStyle: conditionals
@ -33,6 +36,9 @@ Style/HashAsLastArrayItem:
Style/PerlBackrefs: Style/PerlBackrefs:
Enabled: false Enabled: false
Style/SuperWithArgsParentheses:
Enabled: false
Style/TrailingCommaInArguments: Style/TrailingCommaInArguments:
EnforcedStyleForMultiline: comma EnforcedStyleForMultiline: comma

View file

@ -1,6 +1,6 @@
# This configuration was generated by # This configuration was generated by
# `rubocop --auto-gen-config` # `rubocop --auto-gen-config`
# on 2024-10-18 14:41:13 UTC using RuboCop version 1.67.0. # on 2024-10-18 19:44:25 UTC using RuboCop version 1.67.0.
# The point is for the user to remove these configuration records # The point is for the user to remove these configuration records
# one by one as the offenses are removed from the code base. # one by one as the offenses are removed from the code base.
# Note that changes in the inspected code, or installation of new # Note that changes in the inspected code, or installation of new
@ -22,23 +22,10 @@ Layout/LineLength:
Exclude: Exclude:
- 'lib/repubmark/elems/footnote.rb' - 'lib/repubmark/elems/footnote.rb'
# Offense count: 1 # Offense count: 2
# This cop supports safe autocorrection (--autocorrect).
# Configuration parameters: AllowedMethods.
# AllowedMethods: present?, blank?, presence, try, try!, in?
Lint/SafeNavigationChain:
Exclude:
- 'lib/repubmark/elems/article.rb'
# Offense count: 1
# Configuration parameters: AllowedMethods, AllowedPatterns, CountRepeatedAttributes. # Configuration parameters: AllowedMethods, AllowedPatterns, CountRepeatedAttributes.
Metrics/AbcSize: Metrics/AbcSize:
Max: 35 Max: 36
# Offense count: 1
# Configuration parameters: CountComments, CountAsOne.
Metrics/ClassLength:
Max: 104
# Offense count: 1 # Offense count: 1
# Configuration parameters: AllowedMethods, AllowedPatterns. # Configuration parameters: AllowedMethods, AllowedPatterns.
@ -48,32 +35,14 @@ Metrics/CyclomaticComplexity:
# Offense count: 5 # Offense count: 5
# Configuration parameters: CountComments, CountAsOne, AllowedMethods, AllowedPatterns. # Configuration parameters: CountComments, CountAsOne, AllowedMethods, AllowedPatterns.
Metrics/MethodLength: Metrics/MethodLength:
Max: 33 Max: 31
# Offense count: 1 # Offense count: 1
# Configuration parameters: AllowedMethods, AllowedPatterns. # Configuration parameters: AllowedMethods, AllowedPatterns.
Metrics/PerceivedComplexity: Metrics/PerceivedComplexity:
Max: 11 Max: 11
# Offense count: 2 # Offense count: 36
Performance/MapMethodChain:
Exclude:
- 'lib/repubmark/elems/footnotes_category.rb'
# Offense count: 14
# This cop supports safe autocorrection (--autocorrect).
# Configuration parameters: AllowOnlyRestArgument, UseAnonymousForwarding, RedundantRestArgumentNames, RedundantKeywordRestArgumentNames, RedundantBlockArgumentNames.
# RedundantRestArgumentNames: args, arguments
# RedundantKeywordRestArgumentNames: kwargs, options, opts
# RedundantBlockArgumentNames: blk, block, proc
Style/ArgumentsForwarding:
Exclude:
- 'lib/repubmark/elems/canvas.rb'
- 'lib/repubmark/elems/footnotes_category.rb'
- 'lib/repubmark/elems/joint.rb'
- 'lib/repubmark/elems/link.rb'
# Offense count: 34
# Configuration parameters: AllowedConstants. # Configuration parameters: AllowedConstants.
Style/Documentation: Style/Documentation:
Enabled: false Enabled: false
@ -97,33 +66,3 @@ Style/RedundantFreeze:
Exclude: Exclude:
- 'lib/repubmark/elems/fraction.rb' - 'lib/repubmark/elems/fraction.rb'
- 'lib/repubmark/elems/power.rb' - 'lib/repubmark/elems/power.rb'
# Offense count: 2
# This cop supports safe autocorrection (--autocorrect).
Style/RedundantStringEscape:
Exclude:
- 'lib/repubmark/elems/footnote.rb'
# Offense count: 1
# This cop supports safe autocorrection (--autocorrect).
# Configuration parameters: AllowModifier.
Style/SoleNestedConditional:
Exclude:
- 'lib/repubmark/elems/footnote.rb'
# Offense count: 7
# This cop supports safe autocorrection (--autocorrect).
Style/SuperArguments:
Exclude:
- 'lib/repubmark/elems/annotation.rb'
- 'lib/repubmark/elems/blockquote.rb'
- 'lib/repubmark/elems/canvas.rb'
- 'lib/repubmark/elems/caption.rb'
- 'lib/repubmark/elems/figures.rb'
- 'lib/repubmark/elems/joint.rb'
- 'lib/repubmark/elems/paragraph.rb'
# Offense count: 24
# This cop supports safe autocorrection (--autocorrect).
Style/SuperWithArgsParentheses:
Enabled: false

View file

@ -6,7 +6,7 @@ source 'https://rubygems.org'
gemspec gemspec
group :development do group :development do
gem 'bundler', '~> 2.4' gem 'bundler', '~> 2.5'
gem 'pry', '~> 0.14' gem 'pry', '~> 0.14'
gem 'rake', '~> 13.2' gem 'rake', '~> 13.2'
gem 'rubocop', '~> 1.67' gem 'rubocop', '~> 1.67'

View file

@ -27,8 +27,8 @@ task test: :tests
desc 'Run code analysis tools' desc 'Run code analysis tools'
task lint: %i[rubocop yard:cov] task lint: %i[rubocop yard:cov]
desc 'Fix code style (rubocop --auto-correct)' desc 'Fix code style (rubocop --autocorrect)'
task fix: 'rubocop:auto_correct' task fix: 'rubocop:autocorrect'
Rake::Task['release'].prereqs.unshift 'default' Rake::Task['release'].prereqs.unshift 'default'
Rake::Task['release:rubygem_push'].clear Rake::Task['release:rubygem_push'].clear
@ -68,6 +68,6 @@ namespace :yard do
coverage = m[1].to_f.round(2) coverage = m[1].to_f.round(2)
puts "Documentation coverage: #{coverage}%" puts "Documentation coverage: #{coverage}%"
raise 'Not fully documented!' if coverage < 38 raise 'Not fully documented!' if coverage < 36
end end
end end

View file

@ -16,6 +16,9 @@ require_relative 'repubmark/titled_ref'
require_relative 'repubmark/config' require_relative 'repubmark/config'
require_relative 'repubmark/config/css_classes' require_relative 'repubmark/config/css_classes'
require_relative 'repubmark/references/author'
require_relative 'repubmark/references/item'
require_relative 'repubmark/elems/base' require_relative 'repubmark/elems/base'
# Top-level element # Top-level element
@ -72,7 +75,10 @@ require_relative 'repubmark/elems/footnote'
module Repubmark module Repubmark
FORMATS = %i[chapters gemtext html summary_plain word_count].freeze FORMATS = %i[chapters gemtext html summary_plain word_count].freeze
SLUG_RE = /\A[[:word:]]+(-[[:word:]]+)*\z/ SLUG_RE = /\A[[:word:]]+(-[[:word:]]+)*\z/
DOI_RE = %r{\A10(\.\d+)+/\w+(\.\w+)*\z}
ISBN_RE = /\A[0-9]{13}\z/
NAME_PART_RE = /\A[[:word:]]+(-[[:word:]]+)*( [[:word:]]+(-[[:word:]]+)*)*\z/
UNICODE_SUPS = { UNICODE_SUPS = {
'0' => '⁰', '0' => '⁰',
@ -117,6 +123,46 @@ module Repubmark
slug slug
end end
def self.validate_doi!(doi)
doi = String(doi).freeze
raise 'Invalid DOI' unless DOI_RE.match? doi
doi
end
def self.validate_isbn!(isbn)
isbn = String(isbn).freeze
raise 'Invalid ISBN' unless ISBN_RE.match? isbn
isbn
end
def self.validate_name_part!(name_part)
name_part = String(name_part).freeze
raise 'Invalid name part' unless NAME_PART_RE.match? name_part
name_part
end
def self.validate_text!(str)
str = String(str).freeze
raise 'Invalid text' if str.empty? || str != str.strip
str
end
def self.validate_absolute_url!(url)
url = Addressable::URI.parse(url).freeze
raise 'Invalid URL' unless url.absolute? &&
url.scheme &&
!url.scheme.strip.empty? &&
url.host &&
!url.host.strip.empty? &&
url.userinfo.nil?
url
end
def self.unicode_sup(val) def self.unicode_sup(val)
String(val).each_char.map { |chr| UNICODE_SUPS.fetch chr }.join.freeze String(val).each_char.map { |chr| UNICODE_SUPS.fetch chr }.join.freeze
end end

View file

@ -6,7 +6,7 @@ module Repubmark
parents :Article parents :Article
def initialize(parent) def initialize(parent)
super parent super
@canvas = Canvas.new self @canvas = Canvas.new self
end end

View file

@ -45,7 +45,7 @@ module Repubmark
].compact.join("\n\n\n").freeze ].compact.join("\n\n\n").freeze
end end
def chapters = @chapter&.chapters[:chapters] || [].freeze def chapters = (@chapter&.chapters || {})[:chapters] || [].freeze
################### ###################
# Builder methods # # Builder methods #

View file

@ -6,7 +6,7 @@ module Repubmark
parents :Canvas parents :Canvas
def initialize(parent) def initialize(parent)
super parent super
@canvas = Canvas.new self @canvas = Canvas.new self
@caption = nil @caption = nil

View file

@ -6,7 +6,7 @@ module Repubmark
parents :Annotation, :Blockquote, :Chapter parents :Annotation, :Blockquote, :Chapter
def initialize(parent) def initialize(parent)
super parent super
@items = [] @items = []
end end
@ -41,14 +41,14 @@ module Repubmark
nil nil
end end
def code_block(*args, **kwargs) def code_block(*, **)
code_block = CodeBlock.new self, *args, **kwargs code_block = CodeBlock.new(self, *, **)
@items << code_block @items << code_block
nil nil
end end
def iframe(*args, **kwargs) def iframe(*, **)
iframe = Iframe.new self, *args, **kwargs iframe = Iframe.new(self, *, **)
@items << iframe @items << iframe
nil nil
end end

View file

@ -8,7 +8,7 @@ module Repubmark
parents :Blockquote, :Figure, :Footnote, :ListItem, :Paragraph parents :Blockquote, :Figure, :Footnote, :ListItem, :Paragraph
def initialize(parent) def initialize(parent)
super parent super
@items = [] @items = []
end end

View file

@ -6,7 +6,7 @@ module Repubmark
parents :Canvas parents :Canvas
def initialize(parent) def initialize(parent)
super parent super
@figures = [] @figures = []
end end

View file

@ -30,12 +30,10 @@ module Repubmark
end end
if url if url
scheme = Addressable::URI.parse(url).scheme&.downcase scheme = Addressable::URI.parse(url).scheme&.downcase
if scheme if scheme && !%w[http https].include?(scheme)
unless %w[http https].include? scheme result += %(<span class="badge text-bg-secondary">\n)
result += %(<span class="badge text-bg-secondary">\n) result += %(#{scheme}://\n)
result += %(#{scheme}://\n) result += "</span>\n"
result += "</span>\n"
end
end end
result += %(<a href="#{url}">#{link_text}</a>\n) result += %(<a href="#{url}">#{link_text}</a>\n)
else else
@ -45,7 +43,7 @@ module Repubmark
if alt_urls&.any? if alt_urls&.any?
result += %[(#{ result += %[(#{
alt_urls.map do |alt_url| alt_urls.map do |alt_url|
%(<a href=\"#{alt_url[:url]}\">#{alt_url[:name]}</a>) %(<a href="#{alt_url[:url]}">#{alt_url[:name]}</a>)
end.join ', ' end.join ', '
})\n] })\n]
end end

View file

@ -20,7 +20,7 @@ module Repubmark
<h2>#@title</h2> <h2>#@title</h2>
<ol> <ol>
#{@footnotes.map(&:to_html).map(&:strip).join("\n")} #{@footnotes.map { |footnote| footnote.to_html.strip }.join("\n")}
</ol> </ol>
HTML HTML
end end
@ -29,7 +29,7 @@ module Repubmark
<<~GEMTEXT <<~GEMTEXT
## #@title ## #@title
#{@footnotes.map(&:to_gemtext).map(&:strip).join("\n")} #{@footnotes.map { |footnote| footnote.to_gemtext.strip }.join("\n")}
GEMTEXT GEMTEXT
end end
@ -37,8 +37,8 @@ module Repubmark
# Builder methods # # Builder methods #
################### ###################
def footnote(**kwargs) def footnote(**)
footnote = Footnote.new(self, **kwargs) footnote = Footnote.new(self, **)
@footnotes << footnote @footnotes << footnote
yield footnote if block_given? yield footnote if block_given?
nil nil

View file

@ -6,7 +6,7 @@ module Repubmark
parents :Caption, :Quote parents :Caption, :Quote
def initialize(parent) def initialize(parent)
super parent super
@raw1 = nil @raw1 = nil
@base = nil @base = nil
@ -92,7 +92,7 @@ module Repubmark
base quote base quote
end end
def section(*args) = base Section.new self, *args def section(*) = base Section.new(self, *)
def text(str) = base Text.new self, str def text(str) = base Text.new self, str

View file

@ -7,8 +7,8 @@ module Repubmark
SCHEMES = %w[http https].freeze SCHEMES = %w[http https].freeze
def initialize(parent, str, uri, **kwargs) def initialize(parent, str, uri, **)
super parent, str, **kwargs super(parent, str, **)
self.uri = uri self.uri = uri
validate_uri! validate_uri!

View file

@ -6,7 +6,7 @@ module Repubmark
parents :Canvas parents :Canvas
def initialize(parent) def initialize(parent)
super parent super
@caption = Caption.new self @caption = Caption.new self
end end

View file

@ -0,0 +1,31 @@
# frozen_string_literal: true
module Repubmark
module References
class Author
attr_reader :family, :given
def initialize(family, given)
self.family = family
self.given = given
end
def inspect = "#<#{self.class}: #{self}>".freeze
def to_s = "#{family}, #{given.join(' ')}".freeze
alias to_str to_s
private
def family=(family)
@family = Repubmark.validate_name_part! family
end
def given=(given)
@given =
Array(given).map { |part| Repubmark.validate_name_part! part }.freeze
end
end
end
end

View file

@ -0,0 +1,52 @@
# frozen_string_literal: true
module Repubmark
module References
class Item
ATTRIBUTES = %i[slug doi isbn title urls authors].freeze
attr_reader(*ATTRIBUTES)
def initialize(**kwargs)
ATTRIBUTES.each { |attr| send :"#{attr}=", kwargs[attr] }
end
def inspect = "#<#{self.class}:#{slug}>".freeze
private
def slug=(slug)
@slug = Repubmark.validate_slug! slug
end
def doi=(doi)
@doi = Repubmark.validate_doi! doi if doi
end
def isbn=(isbn)
@isbn = Repubmark.validate_isbn! isbn if isbn
end
def title=(title)
@title = Repubmark.validate_text! title
end
def urls=(urls)
urls = Array urls
return @urls = [].freeze if urls.empty?
@urls = urls.map { |url| Repubmark.validate_absolute_url! url }.freeze
end
def authors=(authors)
authors = Array authors
return @authors = [].freeze if authors.empty?
@authors = authors.freeze.each do |author|
unless author.instance_of? Author
raise TypeError, "Expected #{Author}, got #{author.class}"
end
end
end
end
end
end

View file

@ -14,7 +14,7 @@ Gem::Specification.new do |spec|
spec.license = 'MIT' spec.license = 'MIT'
spec.homepage = home spec.homepage = home
spec.required_ruby_version = '~> 3.2' spec.required_ruby_version = '~> 3.3'
spec.authors = ['Alex Kotov'] spec.authors = ['Alex Kotov']
spec.email = [mail] spec.email = [mail]