monograph 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +3 -0
- data/Gemfile.lock +28 -0
- data/Rakefile +7 -0
- data/bin/monograph +19 -0
- data/lib/monograph.rb +23 -0
- data/lib/monograph/book.rb +52 -0
- data/lib/monograph/book_template_context.rb +53 -0
- data/lib/monograph/chapter.rb +48 -0
- data/lib/monograph/chapter_template_context.rb +40 -0
- data/lib/monograph/cli.rb +32 -0
- data/lib/monograph/export.rb +78 -0
- data/lib/monograph/markdown_renderer.rb +37 -0
- data/lib/monograph/template_context.rb +26 -0
- data/lib/monograph/version.rb +3 -0
- data/monograph.gemspec +21 -0
- data/templates/default/assets/images/icon-info.svg +15 -0
- data/templates/default/assets/images/icon-warning.svg +18 -0
- data/templates/default/assets/images/icon.svg +1 -0
- data/templates/default/assets/reset.css +118 -0
- data/templates/default/assets/stylesheet.scss +160 -0
- data/templates/default/contents.html +17 -0
- data/templates/default/index.html +11 -0
- data/templates/default/template.html +33 -0
- data/test/book_test.rb +30 -0
- data/test/chapter_test.rb +34 -0
- data/test/cli_test.rb +24 -0
- data/test/export_test.rb +34 -0
- data/test/fixtures/book1/01-introduction.md +7 -0
- data/test/fixtures/book1/02-dogs.md +53 -0
- data/test/fixtures/book1/03-cats.md +12 -0
- data/test/fixtures/book1/assets/images/dog.jpg +0 -0
- data/test/fixtures/book1/assets/images/flowchart.png +0 -0
- data/test/fixtures/book1/assets/images/penguin.jpg +0 -0
- data/test/fixtures/book1/config.yml +13 -0
- data/test/fixtures/book2/01-bananas.md +3 -0
- data/test/fixtures/book2/02-apples.md +5 -0
- data/test/fixtures/book2/config.yml +10 -0
- data/test/fixtures/book2/template.html +4 -0
- data/test/test_helper.rb +4 -0
- metadata +148 -0
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
monograph (1.0.0)
|
5
|
+
pygments.rb (~> 0.5.0)
|
6
|
+
rake (~> 10.0.4)
|
7
|
+
redcarpet (~> 2.2.2)
|
8
|
+
sass (~> 3.2.9)
|
9
|
+
|
10
|
+
GEM
|
11
|
+
remote: https://rubygems.org/
|
12
|
+
specs:
|
13
|
+
posix-spawn (0.3.6)
|
14
|
+
pygments.rb (0.5.0)
|
15
|
+
posix-spawn (~> 0.3.6)
|
16
|
+
yajl-ruby (~> 1.1.0)
|
17
|
+
rake (10.0.4)
|
18
|
+
redcarpet (2.2.2)
|
19
|
+
sass (3.2.9)
|
20
|
+
test-unit (2.5.5)
|
21
|
+
yajl-ruby (1.1.0)
|
22
|
+
|
23
|
+
PLATFORMS
|
24
|
+
ruby
|
25
|
+
|
26
|
+
DEPENDENCIES
|
27
|
+
monograph!
|
28
|
+
test-unit
|
data/Rakefile
ADDED
data/bin/monograph
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
$:.unshift(File.expand_path('../../lib', __FILE__))
|
3
|
+
require 'monograph'
|
4
|
+
|
5
|
+
if ARGV.first.nil?
|
6
|
+
puts "usage: monograph [command]"
|
7
|
+
exit 1
|
8
|
+
end
|
9
|
+
|
10
|
+
cli = Monograph::CLI.new
|
11
|
+
begin
|
12
|
+
cli.send(*ARGV)
|
13
|
+
rescue NoMethodError => e
|
14
|
+
puts "Invalid command '#{ARGV.first}'"
|
15
|
+
exit 1
|
16
|
+
rescue Monograph::CLI::Error => e
|
17
|
+
puts "\e[31m#{e.message}\e[0m"
|
18
|
+
exit 1
|
19
|
+
end
|
data/lib/monograph.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'redcarpet'
|
2
|
+
require 'pygments'
|
3
|
+
require 'yaml'
|
4
|
+
require 'erb'
|
5
|
+
require 'sass'
|
6
|
+
|
7
|
+
require 'monograph/version'
|
8
|
+
require 'monograph/book'
|
9
|
+
require 'monograph/chapter'
|
10
|
+
require 'monograph/export'
|
11
|
+
require 'monograph/template_context'
|
12
|
+
require 'monograph/chapter_template_context'
|
13
|
+
require 'monograph/book_template_context'
|
14
|
+
require 'monograph/markdown_renderer'
|
15
|
+
require 'monograph/cli'
|
16
|
+
|
17
|
+
module Monograph
|
18
|
+
|
19
|
+
def self.root
|
20
|
+
File.expand_path('../../', __FILE__)
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
module Monograph
|
2
|
+
class Book
|
3
|
+
|
4
|
+
attr_reader :path
|
5
|
+
|
6
|
+
def initialize(path)
|
7
|
+
@path = path
|
8
|
+
end
|
9
|
+
|
10
|
+
def config_path
|
11
|
+
File.join(@path, 'config.yml')
|
12
|
+
end
|
13
|
+
|
14
|
+
def title
|
15
|
+
config['title']
|
16
|
+
end
|
17
|
+
|
18
|
+
def config
|
19
|
+
@config ||= YAML.load(File.read(config_path))
|
20
|
+
end
|
21
|
+
|
22
|
+
def chapters
|
23
|
+
@chapters ||= Dir[File.join(@path, '*.md')].map { |path| Monograph::Chapter.new(self, path) }
|
24
|
+
end
|
25
|
+
|
26
|
+
def builtin_template
|
27
|
+
config['template'] || 'default'
|
28
|
+
end
|
29
|
+
|
30
|
+
def builtin_template_path
|
31
|
+
File.join(Monograph.root, 'templates', builtin_template)
|
32
|
+
end
|
33
|
+
|
34
|
+
def template_file(name)
|
35
|
+
template_path = File.join(@path, name)
|
36
|
+
if File.exist?(template_path)
|
37
|
+
File.read(template_path)
|
38
|
+
else
|
39
|
+
File.read(File.join(builtin_template_path, name))
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def template
|
44
|
+
@template ||= template_file('template.html')
|
45
|
+
end
|
46
|
+
|
47
|
+
def export
|
48
|
+
@export ||= Export.new(self)
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module Monograph
|
2
|
+
# The BookTemplateContext is used whenever a non-chapter page (cover, contents) is to be
|
3
|
+
# rendered within the template.
|
4
|
+
class BookTemplateContext < TemplateContext
|
5
|
+
|
6
|
+
attr_reader :book
|
7
|
+
|
8
|
+
def initialize(book, template)
|
9
|
+
@book = book
|
10
|
+
@template = template
|
11
|
+
end
|
12
|
+
|
13
|
+
def content
|
14
|
+
@content ||= @book.template_file(@template)
|
15
|
+
end
|
16
|
+
|
17
|
+
def page_title
|
18
|
+
title
|
19
|
+
end
|
20
|
+
|
21
|
+
def title
|
22
|
+
case @template
|
23
|
+
when 'index.html' then @book.title
|
24
|
+
when 'contents.html' then 'Contents'
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def author
|
29
|
+
@book.config['author']
|
30
|
+
end
|
31
|
+
|
32
|
+
def permalink
|
33
|
+
@template.gsub(/\.html\z/, '')
|
34
|
+
end
|
35
|
+
|
36
|
+
def next_page
|
37
|
+
case @template
|
38
|
+
when 'index.html'
|
39
|
+
self.class.new(@book, 'contents.html')
|
40
|
+
when 'contents.html'
|
41
|
+
@book.chapters.first
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def previous_page
|
46
|
+
case @template
|
47
|
+
when 'contents.html'
|
48
|
+
self.class.new(@book, 'index.html')
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module Monograph
|
2
|
+
class Chapter
|
3
|
+
|
4
|
+
attr_reader :book
|
5
|
+
attr_reader :path
|
6
|
+
|
7
|
+
def initialize(book, path)
|
8
|
+
@book = book
|
9
|
+
@path = path
|
10
|
+
end
|
11
|
+
|
12
|
+
def number
|
13
|
+
@number ||= permalink.split('-').first.to_i
|
14
|
+
end
|
15
|
+
|
16
|
+
def permalink
|
17
|
+
@permalink ||= @path.split('/').last.gsub(/\.md\z/, '')
|
18
|
+
end
|
19
|
+
|
20
|
+
def raw
|
21
|
+
@raw ||= File.open(File.join(@path), 'rb', &:read)
|
22
|
+
end
|
23
|
+
|
24
|
+
def html
|
25
|
+
@html ||= begin
|
26
|
+
rc = Redcarpet::Markdown.new(MarkdownRenderer.new(:with_toc_data => true), :fenced_code_blocks => true)
|
27
|
+
rc.render(self.raw)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def title
|
32
|
+
@title ||= raw =~ /\A\# ([a-z0-9 ]+)$/i ? $1 : 'Unknown'
|
33
|
+
end
|
34
|
+
|
35
|
+
def template_context
|
36
|
+
@template_context ||= ChapterTemplateContext.new(self)
|
37
|
+
end
|
38
|
+
|
39
|
+
def sections(level = 2)
|
40
|
+
items = self.html.scan(/<h#{level} id=\"([a-z0-9\-\_]+)\">(.*?)<\/h#{level}>/m)
|
41
|
+
items.inject({}) do |hash, match|
|
42
|
+
hash[match[0]] = match[1]
|
43
|
+
hash
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module Monograph
|
2
|
+
# The ChapterTemplateContext is used whenever a chapter is to be rendered within the template.
|
3
|
+
class ChapterTemplateContext < TemplateContext
|
4
|
+
|
5
|
+
attr_reader :chapter
|
6
|
+
|
7
|
+
def initialize(chapter)
|
8
|
+
@chapter = chapter
|
9
|
+
end
|
10
|
+
|
11
|
+
def book
|
12
|
+
@chapter.book
|
13
|
+
end
|
14
|
+
|
15
|
+
def page_title
|
16
|
+
"#{chapter.title} - #{book.title}"
|
17
|
+
end
|
18
|
+
|
19
|
+
def content
|
20
|
+
@chapter.html
|
21
|
+
end
|
22
|
+
|
23
|
+
def permalink
|
24
|
+
@chapter.permalink + ".html"
|
25
|
+
end
|
26
|
+
|
27
|
+
def next_page
|
28
|
+
@chapter.book.chapters.select { |c| c.number == @chapter.number + 1}.first
|
29
|
+
end
|
30
|
+
|
31
|
+
def previous_page
|
32
|
+
if @chapter.number == 1
|
33
|
+
BookTemplateContext.new(@chapter.book, 'contents.html')
|
34
|
+
else
|
35
|
+
@chapter.book.chapters.select { |c| c.number == @chapter.number - 1}.first
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module Monograph
|
2
|
+
class CLI
|
3
|
+
|
4
|
+
class Error < StandardError; end
|
5
|
+
|
6
|
+
# Initialize a new book in the given path
|
7
|
+
def init(path = nil)
|
8
|
+
raise Error, "You must specify a path to create your new book in" if path.nil?
|
9
|
+
raise Error, "File already exists at #{path}" if File.exist?(path)
|
10
|
+
FileUtils.mkdir_p(path)
|
11
|
+
FileUtils.cp_r(File.join(Monograph.root, 'test', 'fixtures', 'book1', '.'), path)
|
12
|
+
puts "Initialized new monograph book at #{path}"
|
13
|
+
Book.new(path)
|
14
|
+
end
|
15
|
+
|
16
|
+
# Export a book, assuming we're in the root of it
|
17
|
+
def build(destination = nil, source = nil)
|
18
|
+
source = Dir.pwd if source.nil?
|
19
|
+
destination = File.join(source, 'build') if destination.nil?
|
20
|
+
raise Error, "You are not currently within a Monograph root" unless File.exist?(File.join(source, 'config.yml'))
|
21
|
+
FileUtils.rm_rf(destination)
|
22
|
+
book = Book.new(source)
|
23
|
+
book.export.save(destination)
|
24
|
+
puts
|
25
|
+
puts " Saved build to #{destination}."
|
26
|
+
puts " Open your cover page at file://#{File.expand_path(destination)}/index.html"
|
27
|
+
puts
|
28
|
+
book
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
module Monograph
|
2
|
+
class Export
|
3
|
+
|
4
|
+
class AlreadyExists < StandardError; end
|
5
|
+
|
6
|
+
def initialize(book)
|
7
|
+
@book = book
|
8
|
+
end
|
9
|
+
|
10
|
+
# Return a hash of all HTML files needed for the book. The hash key is
|
11
|
+
# the filename and the value is the full HTML document.
|
12
|
+
def html_files
|
13
|
+
@html ||= begin
|
14
|
+
erb = ERB.new(@book.template)
|
15
|
+
hash = {}
|
16
|
+
|
17
|
+
# export the chapters
|
18
|
+
@book.chapters.inject(hash) do |hash, chapter|
|
19
|
+
hash["#{chapter.permalink}.html"] = erb.result(chapter.template_context.get_binding)
|
20
|
+
hash
|
21
|
+
end
|
22
|
+
|
23
|
+
# export other ancillary pages
|
24
|
+
['index.html', 'contents.html'].each do |page|
|
25
|
+
context = BookTemplateContext.new(@book, page)
|
26
|
+
page_erb = ERB.new(erb.result(context.get_binding))
|
27
|
+
hash[page] = page_erb.result(context.get_binding)
|
28
|
+
end
|
29
|
+
|
30
|
+
hash
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
# Return a hash of all required assets for the book. This combines all
|
35
|
+
# the files from the built-in template with those provided within the
|
36
|
+
# book itself.
|
37
|
+
#
|
38
|
+
# The copy within the book will always win if there's a filename conflict.
|
39
|
+
def assets
|
40
|
+
@assets ||= begin
|
41
|
+
hash = Hash.new
|
42
|
+
search_paths = []
|
43
|
+
search_paths << File.join(@book.builtin_template_path, 'assets', '**', '*')
|
44
|
+
search_paths << File.join(@book.path, 'assets', '**', '*')
|
45
|
+
search_paths.each do |search_path|
|
46
|
+
Dir[search_path].inject(hash) do |hash, path|
|
47
|
+
if File.file?(path)
|
48
|
+
content = File.open(path, 'rb', &:read)
|
49
|
+
case path.split('.').last
|
50
|
+
when 'scss'
|
51
|
+
path.gsub!(/.scss\z/, '.css')
|
52
|
+
content = Sass::Engine.new(content, :syntax => :scss).render
|
53
|
+
end
|
54
|
+
hash[path.gsub(/\A.*\/assets\//, '')] = content
|
55
|
+
end
|
56
|
+
hash
|
57
|
+
end
|
58
|
+
end
|
59
|
+
hash
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
# Actually run the export to the local file system. This method accepts a path and
|
64
|
+
# an optional boolean for forcing the creation.
|
65
|
+
def save(path, force = false)
|
66
|
+
raise AlreadyExists, "Export path already exists at #{path}" if File.exist?(path) && !force
|
67
|
+
FileUtils.rm_rf(path) if File.exist?(path) && force
|
68
|
+
FileUtils.mkdir_p(path)
|
69
|
+
html_files.each { |filename, content| File.open(File.join(path, filename), 'wb') { |f| f.write(content) }}
|
70
|
+
assets.each do |filename, content|
|
71
|
+
full_path = File.join(path, filename)
|
72
|
+
dir = File.dirname(full_path)
|
73
|
+
FileUtils.mkdir_p(dir)
|
74
|
+
File.open(full_path, 'w') { |f| f.write(content) }
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module Monograph
|
2
|
+
class MarkdownRenderer < Redcarpet::Render::HTML
|
3
|
+
|
4
|
+
def block_code(code, language)
|
5
|
+
Pygments.highlight(code, :lexer => language)
|
6
|
+
end
|
7
|
+
|
8
|
+
def paragraph(text)
|
9
|
+
klass = ''
|
10
|
+
text.gsub!(/\A\[([A-Z]+)\]/) do
|
11
|
+
klass = " class='#{$1.downcase}'"
|
12
|
+
''
|
13
|
+
end
|
14
|
+
"<p#{klass}>#{text}</p>"
|
15
|
+
end
|
16
|
+
|
17
|
+
# Images need positioning ability
|
18
|
+
def image(src, title, alt)
|
19
|
+
if alt.gsub!(/\*([\w\-\s]+)\z/, '')
|
20
|
+
klass = "imgcontainer #{$1}"
|
21
|
+
else
|
22
|
+
klass = nil
|
23
|
+
end
|
24
|
+
String.new.tap do |s|
|
25
|
+
s << "<span class='#{klass}'>"
|
26
|
+
if klass == "imgcontainer captioned"
|
27
|
+
s << "<span class='image'><img src='#{src}' title='#{title}' alt='#{alt}'></span>"
|
28
|
+
s << "<span class='caption'>#{alt}</span>"
|
29
|
+
else
|
30
|
+
s << "<img src='#{src}' title='#{title}' alt='#{alt}'>"
|
31
|
+
end
|
32
|
+
s << "</span>"
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Monograph
|
2
|
+
# Template contexts are used to provide ERB templates with access to various methods.
|
3
|
+
# Each type of context must inherit from TemplateContext.
|
4
|
+
class TemplateContext
|
5
|
+
|
6
|
+
def book
|
7
|
+
end
|
8
|
+
|
9
|
+
# This title will be displayed in the <title> attribute on the page.
|
10
|
+
def page_title
|
11
|
+
end
|
12
|
+
|
13
|
+
# This must return either nil or an object which responds to 'permalink' and 'title'.
|
14
|
+
def previous_page
|
15
|
+
end
|
16
|
+
|
17
|
+
# This must return either nil or an object which responds to 'permalink' and 'title'.
|
18
|
+
def next_page
|
19
|
+
end
|
20
|
+
|
21
|
+
def get_binding
|
22
|
+
binding
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
end
|
data/monograph.gemspec
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
$LOAD_PATH.unshift(File.expand_path('../lib', __FILE__))
|
2
|
+
require 'monograph/version'
|
3
|
+
|
4
|
+
Gem::Specification.new do |s|
|
5
|
+
s.name = 'monograph'
|
6
|
+
s.version = Monograph::VERSION
|
7
|
+
s.platform = Gem::Platform::RUBY
|
8
|
+
s.summary = "A beautiful HTML book builder for Markdown"
|
9
|
+
s.description = "A command line tool for generating beautiful HTML eBooks from Markdown documents"
|
10
|
+
s.files = Dir["**/*"]
|
11
|
+
s.add_dependency('redcarpet', '~> 2.2.2')
|
12
|
+
s.add_dependency('pygments.rb', '~> 0.5.0')
|
13
|
+
s.add_dependency('rake', '~> 10.0.4')
|
14
|
+
s.add_dependency('sass', '~> 3.2.9')
|
15
|
+
s.bindir = "bin"
|
16
|
+
s.require_path = 'lib'
|
17
|
+
s.has_rdoc = false
|
18
|
+
s.author = "Adam Cooke"
|
19
|
+
s.email = "adam@atechmedia.com"
|
20
|
+
s.homepage = "http://atechmedia.com"
|
21
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
<?xml version="1.0" encoding="utf-8"?>
|
2
|
+
<!-- Generator: Adobe Illustrator 16.2.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
3
|
+
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
4
|
+
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
5
|
+
width="96.731px" height="97.396px" viewBox="0 0 96.731 97.396" enable-background="new 0 0 96.731 97.396" xml:space="preserve">
|
6
|
+
<g>
|
7
|
+
<path fill="#03779E" d="M47.385,2.399C21.983,2.739,1.667,23.61,2.004,49.015c0.34,25.393,21.209,45.715,46.611,45.377
|
8
|
+
c25.398-0.342,45.719-21.213,45.381-46.615C93.656,22.382,72.785,2.061,47.385,2.399z M52.484,17.729
|
9
|
+
c4.676,0,6.057,2.714,6.057,5.815c0,3.877-3.104,7.461-8.396,7.461c-4.43,0-6.537-2.229-6.414-5.91
|
10
|
+
C43.731,21.992,46.33,17.729,52.484,17.729z M40.497,77.146c-3.195,0-5.537-1.938-3.301-10.467l3.666-15.125
|
11
|
+
c0.637-2.424,0.744-3.393,0-3.393c-0.959,0-5.107,1.674-7.557,3.322l-1.598-2.617c7.773-6.491,16.712-10.299,20.544-10.299
|
12
|
+
c3.195,0,3.727,3.779,2.131,9.594l-4.199,15.9c-0.744,2.811-0.424,3.779,0.318,3.779c0.961,0,4.102-1.166,7.188-3.59l1.811,2.424
|
13
|
+
C51.939,74.233,43.69,77.146,40.497,77.146z"/>
|
14
|
+
</g>
|
15
|
+
</svg>
|
@@ -0,0 +1,18 @@
|
|
1
|
+
<?xml version="1.0" encoding="utf-8"?>
|
2
|
+
<!-- Generator: Adobe Illustrator 16.2.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
3
|
+
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
4
|
+
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
5
|
+
width="96.731px" height="97.396px" viewBox="0 0 96.731 97.396" enable-background="new 0 0 96.731 97.396" xml:space="preserve">
|
6
|
+
<path fill="#EF7D08" d="M88.946,59.506l-8.254-7.356c-2.391-2.131-2.39-5.616,0.001-7.746l8.253-7.355
|
7
|
+
c2.391-2.131,1.753-4.118-1.417-4.42l-10.948-1.036c-3.169-0.301-4.874-3.026-3.784-6.055l5.809-16.173
|
8
|
+
c1.088-3.031-0.244-4.136-2.961-2.452l-12.913,7.986c-2.718,1.681-6.227,0.762-7.799-2.043l-4.677-8.347
|
9
|
+
c-1.573-2.805-4.17-2.82-5.772-0.034l-4.347,7.557c-1.602,2.787-5.287,3.977-8.186,2.645l-7.647-3.513
|
10
|
+
c-2.9-1.332-5.131,0.213-4.958,3.433l0.415,7.723c0.173,3.22-2.204,6.535-5.28,7.363l-7.385,1.986
|
11
|
+
c-3.076,0.83-3.638,3.25-1.248,5.381l8.255,7.355c2.391,2.129,2.391,5.614,0,7.744l-8.255,7.357
|
12
|
+
c-2.391,2.131-1.759,4.187,1.402,4.572l10.016,1.218c3.161,0.387,4.922,3.205,3.915,6.264l-5.138,15.579
|
13
|
+
c-1.008,3.059,0.429,4.251,3.194,2.652l11.777-6.815c2.765-1.599,6.483-0.719,8.262,1.954l4.89,7.353
|
14
|
+
c1.778,2.676,4.447,2.529,5.928-0.325l4.3-8.287c1.482-2.856,5.031-4.023,7.885-2.596l8.292,4.146
|
15
|
+
c2.856,1.425,5.05-0.039,4.877-3.259l-0.415-7.727c-0.172-3.22,2.205-6.533,5.282-7.361l7.382-1.987
|
16
|
+
C90.775,64.056,91.337,61.636,88.946,59.506z M52.178,68.357h-9.563V58.317h9.563V68.357z M52.178,53.058h-9.563V28.195h9.563
|
17
|
+
V53.058z"/>
|
18
|
+
</svg>
|
@@ -0,0 +1 @@
|
|
1
|
+
<?xml version="1.0" encoding="utf-8"?> <!-- Generator: IcoMoon.io --> <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> <svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="48px" height="48px" viewBox="0 0 48 48" enable-background="new 0 0 16 16" xml:space="preserve" fill="#ffffff"> <path d="M 45.00,42.00c-4.59,0.201-13.341,0.987-18.09,3.453 C 26.685,46.887, 25.497,48.00, 24.00,48.00c-1.497,0.00-2.682-1.113-2.91-2.547C 16.344,42.987, 7.59,42.201, 3.00,42.00c-1.656,0.00-3.00-1.341-3.00-3.00L0.00,3.00 c0.00-1.656, 1.344-3.00, 3.00-3.00c 0.105,0.00, 0.192,0.051, 0.297,0.06C 23.928,0.591, 24.00,6.00, 24.00,6.00s 0.072-5.409, 20.703-5.94C 44.808,0.051, 44.895,0.00, 45.00,0.00 c 1.659,0.00, 3.00,1.344, 3.00,3.00l0.00,36.00 C 48.00,40.659, 46.659,42.00, 45.00,42.00z M 21.00,9.402C 17.13,7.434, 10.728,6.615, 6.00,6.267l0.00,29.898 C 14.052,36.573, 18.558,37.779, 21.00,39.00 L21.00,9.402 z M 42.00,6.267c-4.728,0.348-11.13,1.167-15.00,3.135L27.00,39.00 c 2.442-1.221, 6.948-2.427, 15.00-2.835L42.00,6.267 z M 39.00,12.00l0.00,6.00 c0.00,0.00-9.00,0.00-9.00,3.00L30.00,15.00 C 30.00,15.00, 30.00,12.00, 39.00,12.00z M 39.00,24.00l0.00,6.00 c0.00,0.00-9.00,0.00-9.00,3.00L30.00,27.00 C 30.00,27.00, 30.00,24.00, 39.00,24.00z M 18.00,15.00l0.00,6.00 c0.00-3.00-9.00-3.00-9.00-3.00L9.00,12.00 C 18.00,12.00, 18.00,15.00, 18.00,15.00z M 18.00,27.00l0.00,6.00 c0.00-3.00-9.00-3.00-9.00-3.00L9.00,24.00 C 18.00,24.00, 18.00,27.00, 18.00,27.00z" ></path></svg>
|
@@ -0,0 +1,118 @@
|
|
1
|
+
/*
|
2
|
+
HTML5 Reset :: style.css
|
3
|
+
----------------------------------------------------------
|
4
|
+
We have learned much from/been inspired by/taken code where offered from:
|
5
|
+
|
6
|
+
Eric Meyer :: http://meyerweb.com
|
7
|
+
HTML5 Doctor :: http://html5doctor.com
|
8
|
+
and the HTML5 Boilerplate :: http://html5boilerplate.com
|
9
|
+
|
10
|
+
-------------------------------------------------------------------------------*/
|
11
|
+
|
12
|
+
/* Let's default this puppy out
|
13
|
+
-------------------------------------------------------------------------------*/
|
14
|
+
|
15
|
+
html, body, body div, span, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, abbr, address, cite, code, del, dfn, em, img, ins, kbd, q, samp, small, strong, sub, sup, var, b, i, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td, article, aside, figure, footer, header, hgroup, menu, nav, section, time, mark, audio, video, details, summary {
|
16
|
+
margin: 0;
|
17
|
+
padding: 0;
|
18
|
+
border: 0;
|
19
|
+
font-size: 100%;
|
20
|
+
vertical-align: baseline;
|
21
|
+
background: transparent;
|
22
|
+
}
|
23
|
+
|
24
|
+
/* consider resetting the default cursor: https://gist.github.com/murtaugh/5247154 */
|
25
|
+
|
26
|
+
article, aside, figure, footer, header, hgroup, nav, section, details, summary {display: block;}
|
27
|
+
|
28
|
+
/* Responsive images and other embedded objects
|
29
|
+
Note: keeping IMG here will cause problems if you're using foreground images as sprites.
|
30
|
+
If this default setting for images is causing issues, you might want to replace it with a .responsive class instead. */
|
31
|
+
img,
|
32
|
+
object,
|
33
|
+
embed {max-width: 100%;}
|
34
|
+
|
35
|
+
/* force a vertical scrollbar to prevent a jumpy page */
|
36
|
+
html {overflow-y: scroll;}
|
37
|
+
|
38
|
+
/* we use a lot of ULs that aren't bulleted.
|
39
|
+
don't forget to restore the bullets within content. */
|
40
|
+
ul {list-style: none;}
|
41
|
+
|
42
|
+
blockquote, q {quotes: none;}
|
43
|
+
|
44
|
+
blockquote:before,
|
45
|
+
blockquote:after,
|
46
|
+
q:before,
|
47
|
+
q:after {content: ''; content: none;}
|
48
|
+
|
49
|
+
a {margin: 0; padding: 0; font-size: 100%; vertical-align: baseline; background: transparent;}
|
50
|
+
|
51
|
+
del {text-decoration: line-through;}
|
52
|
+
|
53
|
+
abbr[title], dfn[title] {border-bottom: 1px dotted #000; cursor: help;}
|
54
|
+
|
55
|
+
/* tables still need cellspacing="0" in the markup */
|
56
|
+
table {border-collapse: collapse; border-spacing: 0;}
|
57
|
+
th {font-weight: bold; vertical-align: bottom;}
|
58
|
+
td {font-weight: normal; vertical-align: top;}
|
59
|
+
|
60
|
+
hr {display: block; height: 1px; border: 0; border-top: 1px solid #ccc; margin: 1em 0; padding: 0;}
|
61
|
+
|
62
|
+
input, select {vertical-align: middle;}
|
63
|
+
|
64
|
+
pre {
|
65
|
+
white-space: pre; /* CSS2 */
|
66
|
+
white-space: pre-wrap; /* CSS 2.1 */
|
67
|
+
white-space: pre-line; /* CSS 3 (and 2.1 as well, actually) */
|
68
|
+
word-wrap: break-word; /* IE */
|
69
|
+
}
|
70
|
+
|
71
|
+
input[type="radio"] {vertical-align: text-bottom;}
|
72
|
+
input[type="checkbox"] {vertical-align: bottom;}
|
73
|
+
.ie7 input[type="checkbox"] {vertical-align: baseline;}
|
74
|
+
.ie6 input {vertical-align: text-bottom;}
|
75
|
+
|
76
|
+
select, input, textarea {font: 99% sans-serif;}
|
77
|
+
|
78
|
+
table {font-size: inherit; font: 100%;}
|
79
|
+
|
80
|
+
small {font-size: 85%;}
|
81
|
+
|
82
|
+
strong {font-weight: bold;}
|
83
|
+
|
84
|
+
td, td img {vertical-align: top;}
|
85
|
+
|
86
|
+
/* Make sure sup and sub don't screw with your line-heights
|
87
|
+
gist.github.com/413930 */
|
88
|
+
sub, sup {font-size: 75%; line-height: 0; position: relative;}
|
89
|
+
sup {top: -0.5em;}
|
90
|
+
sub {bottom: -0.25em;}
|
91
|
+
|
92
|
+
/* standardize any monospaced elements */
|
93
|
+
pre, code, kbd, samp {font-family: monospace, sans-serif;}
|
94
|
+
|
95
|
+
/* hand cursor on clickable elements */
|
96
|
+
.clickable,
|
97
|
+
label,
|
98
|
+
input[type=button],
|
99
|
+
input[type=submit],
|
100
|
+
input[type=file],
|
101
|
+
button {cursor: pointer;}
|
102
|
+
|
103
|
+
/* Webkit browsers add a 2px margin outside the chrome of form elements */
|
104
|
+
button, input, select, textarea {margin: 0;}
|
105
|
+
|
106
|
+
/* make buttons play nice in IE */
|
107
|
+
button {width: auto; overflow: visible;}
|
108
|
+
|
109
|
+
/* scale images in IE7 more attractively */
|
110
|
+
.ie7 img {-ms-interpolation-mode: bicubic;}
|
111
|
+
|
112
|
+
/* prevent BG image flicker upon hover */
|
113
|
+
.ie6 html {filter: expression(document.execCommand("BackgroundImageCache", false, true));}
|
114
|
+
|
115
|
+
/* let's clear some floats */
|
116
|
+
.clearfix:before, .clearfix:after { content: "\0020"; display: block; height: 0; overflow: hidden; }
|
117
|
+
.clearfix:after { clear: both; }
|
118
|
+
.clearfix { zoom: 1; }
|
@@ -0,0 +1,160 @@
|
|
1
|
+
html {
|
2
|
+
font-family:'Proxima Nova', 'Helvetica Neue', 'Arial', sans-serif;
|
3
|
+
font-size:15px;
|
4
|
+
color:#000;
|
5
|
+
margin:0;
|
6
|
+
background:#efefef;
|
7
|
+
|
8
|
+
a { color:#298EBE; font-weight:600; text-decoration:none; border-bottom:1px solid #298EBE}
|
9
|
+
|
10
|
+
body {
|
11
|
+
margin:0;
|
12
|
+
}
|
13
|
+
|
14
|
+
div.inner {
|
15
|
+
width:800px;
|
16
|
+
margin:auto;
|
17
|
+
}
|
18
|
+
|
19
|
+
header {
|
20
|
+
position:fixed;
|
21
|
+
top:0;
|
22
|
+
background:#efefef;
|
23
|
+
border-bottom:1px solid #ccc;
|
24
|
+
width:100%;
|
25
|
+
padding-bottom:15px;
|
26
|
+
height:65px;
|
27
|
+
div.inner {
|
28
|
+
padding-top:40px;
|
29
|
+
p.left { float:left; font-weight:100;}
|
30
|
+
p.right { float:right; font-weight:100;}
|
31
|
+
p { color:#999; }
|
32
|
+
p a { font-weight:100; color:inherit; border:0;}
|
33
|
+
}
|
34
|
+
}
|
35
|
+
|
36
|
+
section.content {
|
37
|
+
section.cover {
|
38
|
+
background:url(images/icon.svg) #000 no-repeat center 130px;;
|
39
|
+
background-size:103px;
|
40
|
+
color:#fff;
|
41
|
+
padding:300px 0 100px 0;
|
42
|
+
text-align:center;
|
43
|
+
h1 { font-size:400%;font-weight:100; margin-bottom:0; padding:0;}
|
44
|
+
h3 { margin-top:5px; font-size:120%; color:#888; font-weight:100;}
|
45
|
+
h3 a { border:0; color:#888; text-decoration:none;}
|
46
|
+
p.intro {
|
47
|
+
padding:0 20%;
|
48
|
+
margin-top:60px;
|
49
|
+
line-height:1.5;
|
50
|
+
font-style:italic;
|
51
|
+
font-size:120%;
|
52
|
+
}
|
53
|
+
p.start {
|
54
|
+
margin-top:120px;
|
55
|
+
a {
|
56
|
+
border-radius:5px;
|
57
|
+
background:#76c21b;
|
58
|
+
|
59
|
+
padding:7px 20px 5px 20px;
|
60
|
+
border-bottom:3px solid #588d19;
|
61
|
+
font-size:120%;
|
62
|
+
font-weight:600;
|
63
|
+
text-decoration:none;
|
64
|
+
color:#fff;
|
65
|
+
}
|
66
|
+
}
|
67
|
+
}
|
68
|
+
|
69
|
+
overflow:hidden;
|
70
|
+
padding-top:95px;
|
71
|
+
background:#fff;
|
72
|
+
padding-bottom:45px;
|
73
|
+
h1, h2, h3, h4, h5, p, ul, ol, pre, blockquote { margin:20px 0; line-height:1.7;}
|
74
|
+
h1 { font-size:260%; font-weight:100; background:#000; margin-top:0;color:#fff; padding:25px; text-align:center; margin-bottom:30px;}
|
75
|
+
h2 { font-size:180%; border-bottom:1px solid #000; padding-bottom:2px;}
|
76
|
+
h3 { font-size:150%;}
|
77
|
+
h4 { font-size:120%;}
|
78
|
+
h5 { font-size:100%;}
|
79
|
+
ul, ol { margin-left:35px;}
|
80
|
+
ul li { list-style:disc;}
|
81
|
+
|
82
|
+
pre {
|
83
|
+
background:#efefef;
|
84
|
+
white-space:pre;
|
85
|
+
font-family:'Consolas', Monaco, 'Courier New', fixed;
|
86
|
+
font-size:90%;
|
87
|
+
line-height:1.4;
|
88
|
+
padding:15px;
|
89
|
+
border:1px solid #ccc;
|
90
|
+
}
|
91
|
+
|
92
|
+
p code, li code {
|
93
|
+
background:#efefef;
|
94
|
+
white-space:pre;
|
95
|
+
font-family:'Consolas', Monaco, 'Courier New', fixed;
|
96
|
+
font-size:90%;
|
97
|
+
line-height:1.4;
|
98
|
+
padding:2px;
|
99
|
+
border:1px solid #ccc;
|
100
|
+
}
|
101
|
+
|
102
|
+
p span.imgcontainer {
|
103
|
+
display:block;
|
104
|
+
&.left { float:left; margin-right:20px; margin-bottom:20px;}
|
105
|
+
&.right { float:right; margin-left:20px; margin-bottom:20px;}
|
106
|
+
&.captioned {
|
107
|
+
border:1px solid #ccc;
|
108
|
+
text-align:center;
|
109
|
+
span.image {
|
110
|
+
background:#fff;
|
111
|
+
text-align:center;
|
112
|
+
padding:15px 0;
|
113
|
+
display:block;
|
114
|
+
}
|
115
|
+
span.caption { display:block;background:#efefef; color:#999; padding:6px;}
|
116
|
+
}
|
117
|
+
}
|
118
|
+
|
119
|
+
blockquote {
|
120
|
+
border-left:3px solid #000;
|
121
|
+
padding-left:15px;
|
122
|
+
margin-left:30px;
|
123
|
+
font-style:italic;
|
124
|
+
}
|
125
|
+
|
126
|
+
p.note {
|
127
|
+
border:1px solid #aed2e1;
|
128
|
+
padding:15px;
|
129
|
+
color:#00769d;
|
130
|
+
background:url(images/icon-info.svg) #e7f8ff no-repeat 15px 15px;
|
131
|
+
background-size:20px;
|
132
|
+
font-size:90%;
|
133
|
+
padding-left:46px;
|
134
|
+
}
|
135
|
+
|
136
|
+
p.warning {
|
137
|
+
border:1px solid #dedfb2;
|
138
|
+
padding:15px;
|
139
|
+
color:#f17d00;
|
140
|
+
background:url(images/icon-warning.svg) #ffffeb no-repeat 15px 15px;
|
141
|
+
background-size:20px;
|
142
|
+
font-size:90%;
|
143
|
+
padding-left:46px;
|
144
|
+
}
|
145
|
+
|
146
|
+
}
|
147
|
+
|
148
|
+
footer {
|
149
|
+
border-top:1px solid #ccc;
|
150
|
+
padding:15px 0;
|
151
|
+
div.inner {
|
152
|
+
padding-top:15px;
|
153
|
+
font-size:90%;
|
154
|
+
padding-bottom:15px;
|
155
|
+
overflow:hidden;
|
156
|
+
p.left { float:left; }
|
157
|
+
p.right { float:right; }
|
158
|
+
}
|
159
|
+
}
|
160
|
+
}
|
@@ -0,0 +1,17 @@
|
|
1
|
+
<section class='contents'>
|
2
|
+
<h1>Contents</h1>
|
3
|
+
<ul class='chapters'>
|
4
|
+
<% for chapter in book.chapters %>
|
5
|
+
<li>
|
6
|
+
<a href="<%= chapter.permalink %>.html"><%= chapter.title %></a>
|
7
|
+
<% unless chapter.sections.empty? %>
|
8
|
+
<ul>
|
9
|
+
<% for key, title in chapter.sections %>
|
10
|
+
<li><a href="<%=chapter.permalink%>.html#<%=key%>"><%=title%></a></li>
|
11
|
+
<% end %>
|
12
|
+
</ul>
|
13
|
+
<% end %>
|
14
|
+
</li>
|
15
|
+
<% end%>
|
16
|
+
</ul>
|
17
|
+
</section>
|
@@ -0,0 +1,11 @@
|
|
1
|
+
<section class='cover'>
|
2
|
+
<h1><%= book.title %></h1>
|
3
|
+
<h3 class='author'>by <a href='mailto:<%=book.config['author']['email']%>'><%= book.config['author']['name'] %></a></h3>
|
4
|
+
<% if book.config['description'] %>
|
5
|
+
<p class='intro'><%= book.config['description']%></p>
|
6
|
+
<% end %>
|
7
|
+
|
8
|
+
<p class='start'>
|
9
|
+
<a href='contents.html'>Start reading</a>
|
10
|
+
</p>
|
11
|
+
</section>
|
@@ -0,0 +1,33 @@
|
|
1
|
+
<!DOCTYPE>
|
2
|
+
<html>
|
3
|
+
<head>
|
4
|
+
<title><%= page_title %></title>
|
5
|
+
<link rel="stylesheet" href="reset.css" type="text/css" />
|
6
|
+
<link rel="stylesheet" href="stylesheet.css" type="text/css" />
|
7
|
+
</head>
|
8
|
+
<body>
|
9
|
+
<header>
|
10
|
+
<div class='inner'>
|
11
|
+
<p class='left'><a href='contents.html'><%= book.title %></a></p>
|
12
|
+
<%if respond_to?(:chapter)%><p class='right'><%= chapter.title %></p><%end%>
|
13
|
+
</div>
|
14
|
+
</header>
|
15
|
+
<section class='content <%=respond_to?(:chapter) ? 'chapter' + chapter.number.to_s : ''%>'>
|
16
|
+
<div class='inner'>
|
17
|
+
<%= content %>
|
18
|
+
</div>
|
19
|
+
</section>
|
20
|
+
<footer>
|
21
|
+
<div class='inner'>
|
22
|
+
<nav class='chapters'>
|
23
|
+
<% if previous_page %>
|
24
|
+
<p class='left'><a href='<%=previous_page.permalink%>.html'>← <%=previous_page.title%></a></p>
|
25
|
+
<% end %>
|
26
|
+
<% if next_page %>
|
27
|
+
<p class='right'><a href='<%=next_page.permalink%>.html'><%=next_page.title%> →</a></p>
|
28
|
+
<% end %>
|
29
|
+
</nav>
|
30
|
+
</div>
|
31
|
+
</footer>
|
32
|
+
</body>
|
33
|
+
</html>
|
data/test/book_test.rb
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
class BookTest < Test::Unit::TestCase
|
2
|
+
|
3
|
+
def setup
|
4
|
+
@book1 = Monograph::Book.new(File.join(FIXTURES_PATH, 'book1'))
|
5
|
+
@book2 = Monograph::Book.new(File.join(FIXTURES_PATH, 'book2'))
|
6
|
+
end
|
7
|
+
|
8
|
+
def test_that_books_can_be_initialized
|
9
|
+
assert_equal Monograph::Book, @book1.class
|
10
|
+
end
|
11
|
+
|
12
|
+
def test_that_books_have_config
|
13
|
+
assert @book1.config.is_a?(Hash)
|
14
|
+
assert_equal 'Example Book', @book1.config['title']
|
15
|
+
assert_equal 'Adam Cooke', @book1.config['author']['name']
|
16
|
+
assert_equal 2013, @book1.config['copyright']['year']
|
17
|
+
end
|
18
|
+
|
19
|
+
def test_that_books_have_chapers
|
20
|
+
assert_equal 3, @book1.chapters.length
|
21
|
+
assert @book1.chapters.all? { |c| c.is_a?(Monograph::Chapter) }
|
22
|
+
end
|
23
|
+
|
24
|
+
def test_that_books_have_a_template
|
25
|
+
assert_equal File.read(File.join(Monograph.root, 'templates', 'default', 'template.html')), @book1.template
|
26
|
+
assert_equal File.read(File.join(@book2.path, 'template.html')), @book2.template
|
27
|
+
assert @book2.template =~ /<div class='demo-template'>/
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
class ChapterTest < Test::Unit::TestCase
|
2
|
+
|
3
|
+
def setup
|
4
|
+
@book = Monograph::Book.new(File.join(FIXTURES_PATH, 'book1'))
|
5
|
+
@chapter = @book.chapters[0]
|
6
|
+
end
|
7
|
+
|
8
|
+
def test_that_chapters_have_access_to_its_book
|
9
|
+
assert_equal @book, @chapter.book
|
10
|
+
end
|
11
|
+
|
12
|
+
def test_that_chapters_have_a_titles
|
13
|
+
assert_equal 'Introduction', @chapter.title
|
14
|
+
assert_equal ['Introduction', 'Dogs', 'Cats'], @book.chapters.map(&:title)
|
15
|
+
end
|
16
|
+
|
17
|
+
def test_that_chapters_have_numbers
|
18
|
+
assert_equal 1, @chapter.number
|
19
|
+
assert_equal [1,2,3], @book.chapters.map(&:number)
|
20
|
+
end
|
21
|
+
|
22
|
+
def test_that_chapters_have_html_content
|
23
|
+
assert @chapter.html =~ /<h1.*>Introduction<\/h1>$/
|
24
|
+
assert @chapter.html =~ /<p>Lorem\s/
|
25
|
+
end
|
26
|
+
|
27
|
+
def test_that_chapters_have_sections
|
28
|
+
assert @book.chapters[1].sections.is_a?(Hash)
|
29
|
+
assert @book.chapters[1].sections['a-secondary-heading-for-this-page'] = 'A secondary heading for this page'
|
30
|
+
assert @book.chapters[1].sections['another-secondary-heading'] = 'Another secondary heading'
|
31
|
+
assert @book.chapters[1].sections['a-final-secondary-heading'] = 'A final secondary heading'
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
data/test/cli_test.rb
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
class CLITest < Test::Unit::TestCase
|
2
|
+
|
3
|
+
def setup
|
4
|
+
@book = Monograph::Book.new(File.join(FIXTURES_PATH, 'book1'))
|
5
|
+
@cli = Monograph::CLI.new
|
6
|
+
@test_path = "/tmp/monograph-new-book"
|
7
|
+
FileUtils.rm_rf(@test_path)
|
8
|
+
end
|
9
|
+
|
10
|
+
def test_init
|
11
|
+
new_book = @cli.init(@test_path)
|
12
|
+
assert_equal Monograph::Book, new_book.class
|
13
|
+
assert File.directory?(@test_path)
|
14
|
+
assert File.file?(File.join(@test_path, 'config.yml'))
|
15
|
+
end
|
16
|
+
|
17
|
+
def test_saving
|
18
|
+
@cli.init(@test_path)
|
19
|
+
assert_equal Monograph::Book, @cli.build(nil, @test_path).class
|
20
|
+
assert File.directory?(File.join(@test_path, 'build'))
|
21
|
+
assert File.file?(File.join(@test_path, 'build', 'index.html'))
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
data/test/export_test.rb
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
class ExportTest < Test::Unit::TestCase
|
2
|
+
def setup
|
3
|
+
@book = Monograph::Book.new(File.join(FIXTURES_PATH, 'book1'))
|
4
|
+
@export = @book.export
|
5
|
+
end
|
6
|
+
|
7
|
+
def test_html_files_hash_is_present
|
8
|
+
assert_equal Hash, @export.html_files.class
|
9
|
+
assert_equal ['01-introduction.html','02-dogs.html','03-cats.html', 'index.html', 'contents.html'], @export.html_files.keys
|
10
|
+
end
|
11
|
+
|
12
|
+
def test_html_files_contains_html
|
13
|
+
assert @export.html_files['01-introduction.html'].include?("<title>Introduction - Example Book</title>")
|
14
|
+
assert @export.html_files['01-introduction.html'].include?("Introduction</h1>")
|
15
|
+
assert @export.html_files['01-introduction.html'].include?("<p>Lorem")
|
16
|
+
assert @export.html_files['02-dogs.html'].include?("<title>Dogs - Example Book</title>")
|
17
|
+
assert @export.html_files['03-cats.html'].include?("<title>Cats - Example Book</title>")
|
18
|
+
end
|
19
|
+
|
20
|
+
def test_assets_hash
|
21
|
+
assert_equal Hash, @export.assets.class
|
22
|
+
end
|
23
|
+
|
24
|
+
def test_export_saving
|
25
|
+
tmp_path = "/tmp/monograph-book-export"
|
26
|
+
FileUtils.rm_rf(tmp_path)
|
27
|
+
assert @book.export.save(tmp_path)
|
28
|
+
assert File.file?(File.join(tmp_path, '01-introduction.html'))
|
29
|
+
assert File.file?(File.join(tmp_path, 'images/penguin.jpg'))
|
30
|
+
assert File.file?(File.join(tmp_path, 'stylesheet.css'))
|
31
|
+
assert File.file?(File.join(tmp_path, 'index.html'))
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
@@ -0,0 +1,7 @@
|
|
1
|
+
# Introduction
|
2
|
+
|
3
|
+
![Penguin*left](images/penguin.jpg)
|
4
|
+
|
5
|
+
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
|
6
|
+
|
7
|
+
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# Dogs
|
2
|
+
|
3
|
+
![Dog*right](images/dog.jpg)
|
4
|
+
|
5
|
+
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
|
6
|
+
|
7
|
+
* This is an example
|
8
|
+
* list of item which
|
9
|
+
* will be displayed within the page.
|
10
|
+
|
11
|
+
## A secondary heading for this page
|
12
|
+
|
13
|
+
Lorem ipsum dolor sit amet, consectetur **adipisicing elit, sed do eiusmod tempor** incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
|
14
|
+
|
15
|
+
1. Another list of things
|
16
|
+
2. this time they are numbered
|
17
|
+
3. which will sometimes be helpful.
|
18
|
+
|
19
|
+
## Another secondary heading
|
20
|
+
|
21
|
+
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor _incididunt ut labore et dolore magna aliqua_. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
|
22
|
+
|
23
|
+
### A teritary heading
|
24
|
+
|
25
|
+
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
|
26
|
+
|
27
|
+
> This is a quote from something which may or may not be helpful.
|
28
|
+
> It spans multiple lines in the markdown but won't elsewhere.
|
29
|
+
|
30
|
+
|
31
|
+
### Another teritary heading
|
32
|
+
|
33
|
+
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
|
34
|
+
|
35
|
+
~~~ ruby
|
36
|
+
module Monograph
|
37
|
+
class Chapter
|
38
|
+
|
39
|
+
attr_reader :book
|
40
|
+
attr_reader :path
|
41
|
+
|
42
|
+
def initialize(book, path)
|
43
|
+
@book = book
|
44
|
+
@path = path
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
end
|
49
|
+
~~~
|
50
|
+
|
51
|
+
## A final secondary heading
|
52
|
+
|
53
|
+
Lorem ipsum dolor sit amet, consectetur adipisicing `elit, sed do eiusmod tempor incididunt ut labore` et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
|
@@ -0,0 +1,12 @@
|
|
1
|
+
# Cats
|
2
|
+
|
3
|
+
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
|
4
|
+
|
5
|
+
![An example flowchart which can be displayed nicely*captioned](images/flowchart.png)
|
6
|
+
|
7
|
+
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
|
8
|
+
|
9
|
+
[NOTE] This item of information will be displayed as a piece of information. To add these to your own documents, just prefix any paragraph with [NOTE] and we'll take care of the rest. There are also others:
|
10
|
+
|
11
|
+
[WARNING] This is a warning item. The information here should be read and therefore we will display this in a suitable style to suggest urgent attention is nessessary.
|
12
|
+
|
Binary file
|
Binary file
|
Binary file
|
@@ -0,0 +1,13 @@
|
|
1
|
+
title: Example Book
|
2
|
+
description:
|
3
|
+
An interesting look at the behaviours of household pets within the United Kingdom.
|
4
|
+
Fully illustrated with a single picture of a penguin.
|
5
|
+
|
6
|
+
author:
|
7
|
+
name: Adam Cooke
|
8
|
+
email: adam@atechmedia.com
|
9
|
+
|
10
|
+
copyright:
|
11
|
+
owner: Adam Cooke
|
12
|
+
year: 2013
|
13
|
+
|
@@ -0,0 +1,3 @@
|
|
1
|
+
# Bananas
|
2
|
+
|
3
|
+
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
|
@@ -0,0 +1,5 @@
|
|
1
|
+
# Apples
|
2
|
+
|
3
|
+
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
|
4
|
+
|
5
|
+
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
|
data/test/test_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,148 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: monograph
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Adam Cooke
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-06-24 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: redcarpet
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ~>
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: 2.2.2
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ~>
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: 2.2.2
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: pygments.rb
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ~>
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: 0.5.0
|
38
|
+
type: :runtime
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ~>
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: 0.5.0
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: rake
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ~>
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: 10.0.4
|
54
|
+
type: :runtime
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ~>
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 10.0.4
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: sass
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ~>
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: 3.2.9
|
70
|
+
type: :runtime
|
71
|
+
prerelease: false
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ~>
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: 3.2.9
|
78
|
+
description: A command line tool for generating beautiful HTML eBooks from Markdown
|
79
|
+
documents
|
80
|
+
email: adam@atechmedia.com
|
81
|
+
executables: []
|
82
|
+
extensions: []
|
83
|
+
extra_rdoc_files: []
|
84
|
+
files:
|
85
|
+
- bin/monograph
|
86
|
+
- Gemfile
|
87
|
+
- Gemfile.lock
|
88
|
+
- lib/monograph/book.rb
|
89
|
+
- lib/monograph/book_template_context.rb
|
90
|
+
- lib/monograph/chapter.rb
|
91
|
+
- lib/monograph/chapter_template_context.rb
|
92
|
+
- lib/monograph/cli.rb
|
93
|
+
- lib/monograph/export.rb
|
94
|
+
- lib/monograph/markdown_renderer.rb
|
95
|
+
- lib/monograph/template_context.rb
|
96
|
+
- lib/monograph/version.rb
|
97
|
+
- lib/monograph.rb
|
98
|
+
- monograph.gemspec
|
99
|
+
- Rakefile
|
100
|
+
- templates/default/assets/images/icon-info.svg
|
101
|
+
- templates/default/assets/images/icon-warning.svg
|
102
|
+
- templates/default/assets/images/icon.svg
|
103
|
+
- templates/default/assets/reset.css
|
104
|
+
- templates/default/assets/stylesheet.scss
|
105
|
+
- templates/default/contents.html
|
106
|
+
- templates/default/index.html
|
107
|
+
- templates/default/template.html
|
108
|
+
- test/book_test.rb
|
109
|
+
- test/chapter_test.rb
|
110
|
+
- test/cli_test.rb
|
111
|
+
- test/export_test.rb
|
112
|
+
- test/fixtures/book1/01-introduction.md
|
113
|
+
- test/fixtures/book1/02-dogs.md
|
114
|
+
- test/fixtures/book1/03-cats.md
|
115
|
+
- test/fixtures/book1/assets/images/dog.jpg
|
116
|
+
- test/fixtures/book1/assets/images/flowchart.png
|
117
|
+
- test/fixtures/book1/assets/images/penguin.jpg
|
118
|
+
- test/fixtures/book1/config.yml
|
119
|
+
- test/fixtures/book2/01-bananas.md
|
120
|
+
- test/fixtures/book2/02-apples.md
|
121
|
+
- test/fixtures/book2/config.yml
|
122
|
+
- test/fixtures/book2/template.html
|
123
|
+
- test/test_helper.rb
|
124
|
+
homepage: http://atechmedia.com
|
125
|
+
licenses: []
|
126
|
+
post_install_message:
|
127
|
+
rdoc_options: []
|
128
|
+
require_paths:
|
129
|
+
- lib
|
130
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
131
|
+
none: false
|
132
|
+
requirements:
|
133
|
+
- - ! '>='
|
134
|
+
- !ruby/object:Gem::Version
|
135
|
+
version: '0'
|
136
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
137
|
+
none: false
|
138
|
+
requirements:
|
139
|
+
- - ! '>='
|
140
|
+
- !ruby/object:Gem::Version
|
141
|
+
version: '0'
|
142
|
+
requirements: []
|
143
|
+
rubyforge_project:
|
144
|
+
rubygems_version: 1.8.23
|
145
|
+
signing_key:
|
146
|
+
specification_version: 3
|
147
|
+
summary: A beautiful HTML book builder for Markdown
|
148
|
+
test_files: []
|