class Ficrip::Story
Constants
- DOCTYPE
Attributes
metadata[RW]
title[RW]
url[RW]
Public Class Methods
construct(title, author, url) { |s| ... }
click to toggle source
# File lib/ficrip/story.rb, line 27 def self.construct(title, author, url) s = Story::new(title, author, url) yield s s end
new(title, author, url, metadata = {})
click to toggle source
# File lib/ficrip/story.rb, line 20 def initialize(title, author, url, metadata = {}) @title = title @author = author @url = url @metadata = metadata end
Public Instance Methods
add_metadata(key, value)
click to toggle source
# File lib/ficrip/story.rb, line 52 def add_metadata(key, value) @metadata[key] = value self end
bind(version: 3, callback: nil)
click to toggle source
Contract { version: Maybe[Or[2, 3]] }
# File lib/ficrip/story.rb, line 58 def bind(version: 3, callback: nil) book = GEPUB::Book.new('OEPBS/package.opf', 'version' => version.to_f.to_s) book.primary_identifier(@url, 'BookId', 'URL') book.language = I18nData.language_code(@metadata[:language]).downcase book.title = @title book.creator = @author # Cover if it exists if @metadata.key? :cover_url cover = open!(@metadata[:cover_url], 'Referer' => @url) cover_type = FastImage.type(cover) book.add_item(format('img/cover_image.%s', cover_type), cover) .cover_image coverpage = <<-XHTML.strip_heredoc <?xml version="1.0" encoding="utf-8"?> #{DOCTYPE[version]} <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> <title>Cover</title> <style type="text/css" title="override_css"> @page { padding: 0pt; margin:0pt } body { text-align: center; padding:0pt; margin: 0pt; } </style> </head> <body> <div style="text-align: center;"> <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="100%" height="100%" preserveAspectRatio="xMidYMid meet"> <image width="100%" height="100%" xlink:href="cover_image.#{cover_type}"></image> </svg> </div> </body> </html> XHTML end titlepage = <<-XHTML.strip_heredoc <?xml version="1.0" encoding="utf-8"?> #{DOCTYPE[version]} <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> <title>#{@title}</title> <style type="text/css" title="override_css"> .outer { display: table; height: 75%; width: 100%; } .middle { display: table-cell; vertical-align: middle; } .inner { text-align: center; } </style> </head> <body> <div class="outer"><div class="middle"><div class="inner"> <h1>#{@title}</h1> <h3>#{@author}</h3> </div></div></div> </body> </html> XHTML table_of_contents = nil book.ordered do book.add_item('img/coverpage.xhtml') .add_content(StringIO.new(coverpage)) .toc_text(@title) if @metadata.key? :cover_url book.add_item('text/titlepage.xhtml') .add_content(StringIO.new(Nokogiri::XML(titlepage) { |c| c.noblanks }.to_xhtml(indent: 2))) book.add_item('text/infopage.xhtml') .add_content(StringIO.new(Nokogiri::XML(render_metadata) { |c| c.noblanks }.to_xhtml(indent: 2))) .toc_text('About') # We want our TOC to be after the cover and titlepage, but we don't any content # for it yet, so we save it for later. table_of_contents = book.add_item('text/toc.xhtml').toc_text('Table of Contents') chapters.each do |chapter| chapter_num, chapter_title = chapter.match(/^(\d+)\s*[-\\.)]?\s+(.*)/).captures chapter_page = Nokogiri::HTML open!(URI.join(@url, chapter_num)) storytext = chapter_page.css('#storytext').first storytext.xpath('//@noshade').remove storytext.xpath('//@size').remove chapter_xhtml = <<-XHTML.strip_heredoc <?xml version="1.0" encoding="utf-8"?> #{DOCTYPE[version]} <html xmlns="http://www.w3.org/1999/xhtml" xmlns:epub="http://www.idpf.org/2007/ops" xml:lang="en"> <head> <title>c#{chapter_num}</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> </head> <body> #{'<section epub:type="chapter">' if version == 3} <h1 style="text-align:center">#{chapter_title}</h1> #{storytext.children.to_xhtml} #{'</section>' if version == 3} </body> </html> XHTML book.add_item(format('text/chapter%03d.xhtml', chapter_num), nil, "c#{chapter_num}") .add_content(StringIO.new(Nokogiri::XML(chapter_xhtml) { |c| c.noblanks }.to_xhtml(indent: 2))) .toc_text(chapter_title) if callback args = [chapter_num.to_i, chapters.count] n = callback.arity callback.call *(n < 0 ? args : args.take(n)) end end end # This generates a proper Table of Contents page at the start of the book by # removing references to the cover and TOC itself book_copy = book.deep_clone cut_idx = @metadata.key?(:cover_url) ? 3 : 2 book_copy.instance_variable_set(:@toc, book_copy.instance_variable_get(:@toc)[cut_idx..-1]) table_of_contents.add_content(StringIO.new(book_copy.nav_doc)) # Finally, we get to add the actual content # Now that we've generated the TOC page, go through every chapter reference in the # toc and prepend the chapter number. This is for the epub's built-in table of contents book.instance_variable_get(:@toc)[cut_idx..-1].each_with_index do |chap, idx| chap[:text] = "#{idx + 1}. #{chap[:text]}" end add_item('nav.html', StringIO.new(book_copy.nav_doc), 'nav').add_property('nav') if version == 3 book end
method_missing(method_sym, *arguments, &block)
click to toggle source
Calls superclass method
# File lib/ficrip/story.rb, line 33 def method_missing(method_sym, *arguments, &block) if method_sym != :title= && method_sym != :author= && method_sym.to_s.end_with?('=') @metadata.send(:[]=, method_sym[0...-1].to_sym, *arguments, &block) elsif @metadata.key?(method_sym) then @metadata[method_sym] else super end end
render_metadata()
click to toggle source
# File lib/ficrip/story.rb, line 192 def render_metadata data = { 'Rating' => rating, 'Language' => language, 'Genres' => genres.join(', '), 'Characters/Pairings' => characters, 'Chapter count' => format_num(chapter_count), 'Word count' => format_num(word_count), 'Reviews' => "<a href='https://fanfiction.com/r/#{info_id}'>" + format_num(review_count) + '</a>', 'Favorites' => format_num(favs_count), 'Follows' => format_num(follows_count), 'Updated' => updated_date, 'Published' => published_date, 'ID' => info_id } Nokogiri::XML::Builder.new(encoding: 'utf-8') { |doc| doc.html('xmlns' => 'http://www.w3.org/1999/xhtml', 'xml:lang' => 'en') { doc.head { doc.meta 'http-equiv' => 'Content-Type', 'content' => 'text/html; charset=UTF-8' doc.title 'About' } doc.body { doc.p { doc.strong 'Author: '; doc.a(href: author_url) { doc.text @author } } doc.p { doc.strong 'Summary:'; doc.br; doc.text summary } data.each do |k, v| doc.span { doc.strong(k + ':') (v.to_s.start_with? '<a') ? doc << v.to_s : doc.text(' ' + v.to_s) doc.br } end } } }.to_xml end
respond_to_missing?(method_sym, include_private = false)
click to toggle source
Calls superclass method
# File lib/ficrip/story.rb, line 44 def respond_to_missing?(method_sym, include_private = false) @metadata.key?(method_sym) || (method_sym != :title= && method_sym != :author= && method_sym.to_s.end_with?('=')) || super end
Private Instance Methods
format_num(num)
click to toggle source
# File activesupport/lib/active_support/inflector/methods.rb, line 123 def humanize(word, capitalize: false)
result = word.to_s.dup result.sub!(/\A_+/, ''.freeze) result.sub!(/_id\z/, ''.freeze) result.tr!('_'.freeze, ' '.freeze) result.gsub!(/([a-z\d]*)/i) { |match| match.downcase } result.sub!(/\A\w/) { |match| match.upcase } if capitalize result
end
# File lib/ficrip/story.rb, line 253 def format_num(num) num.to_s.reverse.gsub(/...(?=.)/, '\&,').reverse end
handle_url(string)
click to toggle source
# File lib/ficrip/story.rb, line 236 def handle_url(string) if string.start_with? 'https://www.fanfiction.net/u/' "<a href='#{string}'>" end end
open!(*args, &block)
click to toggle source
# File lib/ficrip/story.rb, line 230 def open!(*args, &block) Retryable.retryable(tries: :infinite, on: OpenURI::HTTPError) do open(*args, &block) end end