maliq 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +17 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +115 -0
- data/Rakefile +1 -0
- data/bin/maliq +63 -0
- data/bin/maliq_gepub +154 -0
- data/lib/maliq.rb +5 -0
- data/lib/maliq/converter.rb +85 -0
- data/lib/maliq/file_utils.rb +28 -0
- data/lib/maliq/system_extensions.rb +34 -0
- data/lib/maliq/version.rb +3 -0
- data/maliq.gemspec +22 -0
- data/spec/converter_spec.rb +132 -0
- data/spec/file_utils_spec.rb +103 -0
- data/spec/plugins/calc.rb +16 -0
- data/spec/spec_helper.rb +3 -0
- metadata +102 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2012 kyoendo
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,115 @@
|
|
1
|
+
# Maliq
|
2
|
+
|
3
|
+
Maliq is a markdown, liquid converter for EPUB's xhtml.
|
4
|
+
|
5
|
+
It comes with two command 'maliq' and 'maliq\_gepub'. 'maliq' is a markdown-xhtml converter and 'maliq\_gepub' is a wrapper of gepub gem which is a EPUB generator.
|
6
|
+
|
7
|
+
## Installation
|
8
|
+
|
9
|
+
Add this line to your application's Gemfile:
|
10
|
+
|
11
|
+
gem 'maliq'
|
12
|
+
|
13
|
+
And then execute:
|
14
|
+
|
15
|
+
$ bundle
|
16
|
+
|
17
|
+
Or install it yourself as:
|
18
|
+
|
19
|
+
$ gem install maliq
|
20
|
+
|
21
|
+
## Commands Usage
|
22
|
+
|
23
|
+
Follow the steps below:
|
24
|
+
|
25
|
+
1. Provide your content with markdown format. (ex. chapter01.md)
|
26
|
+
|
27
|
+
2. Write meta data required for EPUB in the head of the file with Yaml Front Matter(YFM) form(see below).
|
28
|
+
|
29
|
+
3. You can obtain separated xhtml files each of which represents a chapter from a markdown file if needed. This achive by placing a special marker into target line of your content. The Default marker is `<<<--- <filename> --->>>`. (ex. `<<<--- chapter02 --->>>`)
|
30
|
+
|
31
|
+
4. Place css and image files into the directory or its sub directory if any.
|
32
|
+
|
33
|
+
5. Place liquid plugins into the sub directory named 'plugins'(default) when your content include liquid tags.
|
34
|
+
|
35
|
+
6. Fire up `maliq` command followed by the filename(s) on the current directory. (ex. maliq chapter01.md) This create xhtml file(s).
|
36
|
+
|
37
|
+
7. Install Gepub gem (gem install gepub), then fire up `maliq_gepub` command to generate a EPUB package.
|
38
|
+
|
39
|
+
## Yaml Front Matter Sample
|
40
|
+
The front matter must be the first thing in the file and takes the form of:
|
41
|
+
|
42
|
+
---
|
43
|
+
language: 'en'
|
44
|
+
unique_identifier:
|
45
|
+
- 'http:/example.jp/bookid_in_url'
|
46
|
+
- 'BookID'
|
47
|
+
- 'URL'
|
48
|
+
title: 'Book of Charlie'
|
49
|
+
subtitle: 'Where Charlie goes to'
|
50
|
+
creator: 'melborne'
|
51
|
+
date: '2012-01-01'
|
52
|
+
---
|
53
|
+
|
54
|
+
Between the triple-dashed lines, you can set predefined variables.
|
55
|
+
|
56
|
+
## Liquid plugins
|
57
|
+
There are many liquid plugins on the Net, but you might need to modify them to be work for Epub generation. Some my modified plugins are there:
|
58
|
+
|
59
|
+
> [Liquid filters for Maliq gem to generate xhtml — Gist](https://gist.github.com/4134497 'Liquid filters for Maliq gem to generate xhtml — Gist')
|
60
|
+
|
61
|
+
|
62
|
+
## Code Usage
|
63
|
+
|
64
|
+
Pass markdown string to `Maliq::Converter.new`.
|
65
|
+
|
66
|
+
puts Maliq::Converter.new("#header1\nline1\n\nline2").run
|
67
|
+
|
68
|
+
This get:
|
69
|
+
|
70
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
71
|
+
<!DOCTYPE html>
|
72
|
+
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:epub="http://www.idpf.org/2007/ops" xml:lang="ja">
|
73
|
+
<head>
|
74
|
+
<title>#{title}</title>
|
75
|
+
|
76
|
+
</head>
|
77
|
+
<body>
|
78
|
+
<h1>header1</h1>
|
79
|
+
|
80
|
+
<p>line1</p>
|
81
|
+
|
82
|
+
<p>line2</p>
|
83
|
+
</body>
|
84
|
+
</html>
|
85
|
+
|
86
|
+
To make liquid tags parsed with plugins, specify a plugin folder at the front matter or set by `#set_meta`. It is not required when the folder is 'plugins'(default).
|
87
|
+
|
88
|
+
Maliq::Converter.new(<<-EOS).run(false)
|
89
|
+
---
|
90
|
+
liquid: 'filters'
|
91
|
+
---
|
92
|
+
# header1
|
93
|
+
{% calc 2 + 3 %}
|
94
|
+
EOS
|
95
|
+
|
96
|
+
This produce followings, with calc.rb plugin at a folder named 'filters'.
|
97
|
+
|
98
|
+
<h1>header1</h1>
|
99
|
+
|
100
|
+
<p>2 + 3 = 5</p>
|
101
|
+
|
102
|
+
|
103
|
+
## Thank you
|
104
|
+
|
105
|
+
Thank you to [Satoshi KOJIMA](https://github.com/skoji) for creating [Ggepub](https://github.com/skoji/gepub 'skoji/gepub') which is a great EPUB generator.
|
106
|
+
|
107
|
+
Thank you to [rtomayko (Ryan Tomayko)](https://github.com/rtomayko) for creating [Rdiscount](https://github.com/rtomayko/rdiscount 'rtomayko/rdiscount') which is a great markdown parser.
|
108
|
+
|
109
|
+
## Contributing
|
110
|
+
|
111
|
+
1. Fork it
|
112
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
113
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
114
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
115
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
data/bin/maliq
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require "maliq"
|
3
|
+
require "trollop"
|
4
|
+
|
5
|
+
class OptionParser
|
6
|
+
def self.parse!
|
7
|
+
opts = build_opts
|
8
|
+
|
9
|
+
filenames = ARGV.select { |f| f.match /\.(md|markdown)$/ }
|
10
|
+
if filenames.empty?
|
11
|
+
abort "Must pass one or more markdown filenames to build xhtml output."
|
12
|
+
end
|
13
|
+
csses = Dir['*.css', '*/*.css']
|
14
|
+
|
15
|
+
filenames.each do |fname|
|
16
|
+
chapters = Maliq::FileUtils.split(fname)
|
17
|
+
chapters.each do |title, text|
|
18
|
+
dest = title.basename_with(:xhtml)
|
19
|
+
conv = Maliq::Converter.new(text, css:csses, liquid:opts[:liquid])
|
20
|
+
conv.save(dest)
|
21
|
+
puts "'#{dest}' created."
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.build_opts
|
27
|
+
Trollop::options do
|
28
|
+
version "Maliq #{Maliq::VERSION} (c) 2012 kyoendo"
|
29
|
+
banner ~<<-EOS
|
30
|
+
Maliq is a markdown, liquid converter for EPUB's xhtml.
|
31
|
+
|
32
|
+
Prerequisite:
|
33
|
+
|
34
|
+
1. Set title and language in Yaml Front Matter(YFM) of
|
35
|
+
your markdown file, which will be used in the header
|
36
|
+
of generating xhtml.
|
37
|
+
|
38
|
+
2. To parse liquid tags in your markdown, place the plugins
|
39
|
+
into the sub directory named 'plugins'.
|
40
|
+
|
41
|
+
3. Place css files into the target directory or its
|
42
|
+
sub-directory if any.
|
43
|
+
|
44
|
+
4. To split your markdown into several xhtmls for building
|
45
|
+
chapters, set special markers "<<<--- <filename> --->>>"
|
46
|
+
into the right places. ex. <<<--- chapter02 --->>>
|
47
|
+
|
48
|
+
|
49
|
+
Usage:
|
50
|
+
|
51
|
+
maliq [options] <filenames>
|
52
|
+
|
53
|
+
ex. maliq chapter01.md
|
54
|
+
|
55
|
+
where [options] are:
|
56
|
+
EOS
|
57
|
+
|
58
|
+
opt :liquid, "Liquid plugin path", :default => 'plugins'
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
OptionParser.parse!
|
data/bin/maliq_gepub
ADDED
@@ -0,0 +1,154 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# encoding: UTF-8
|
3
|
+
require "maliq"
|
4
|
+
require "trollop"
|
5
|
+
require "gepub"
|
6
|
+
require "stringio"
|
7
|
+
|
8
|
+
# a patch for Nokogiri::XML::Node#namespaces
|
9
|
+
# Hash[ alist ] => Hash[ *alist.flatten ]
|
10
|
+
class Nokogiri::XML::Node
|
11
|
+
def namespaces
|
12
|
+
Hash[*namespace_scopes.map { |nd|
|
13
|
+
key = ['xmlns', nd.prefix].compact.join(':')
|
14
|
+
if RUBY_VERSION >= '1.9' && document.encoding
|
15
|
+
begin
|
16
|
+
key.force_encoding document.encoding
|
17
|
+
rescue ArgumentError
|
18
|
+
end
|
19
|
+
end
|
20
|
+
[key, nd.href]
|
21
|
+
}.flatten]
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
class OptionParser
|
26
|
+
class << self
|
27
|
+
def parse!
|
28
|
+
opts = build_opts
|
29
|
+
|
30
|
+
files = read_files
|
31
|
+
markdowns = files.values_atx(:md, :markdown)
|
32
|
+
metadata = read_metadata(markdowns.first)
|
33
|
+
generate(metadata, files, opts)
|
34
|
+
end
|
35
|
+
|
36
|
+
def build_opts
|
37
|
+
Trollop::options do
|
38
|
+
version "Maliq #{Maliq::VERSION} (c) 2012 kyoendo"
|
39
|
+
banner ~<<-EOS
|
40
|
+
Maliq is a markdown, liquid converter for EPUB's xhtml.
|
41
|
+
|
42
|
+
'maliq_gepub' command is a tool for generating a EPUB package
|
43
|
+
using Gepub gem.
|
44
|
+
|
45
|
+
Usage:
|
46
|
+
|
47
|
+
1. Install gepub gem(gem install gepub)
|
48
|
+
|
49
|
+
2. Set meta data required for Epub generation in Yaml Front
|
50
|
+
Matter(YFM) of your markdown file.
|
51
|
+
|
52
|
+
3. Create xhtml files with 'maliq' command(see maliq -h).
|
53
|
+
|
54
|
+
4. fire up 'maliq_gepub' on the direcroty.
|
55
|
+
|
56
|
+
where [options] are:
|
57
|
+
EOS
|
58
|
+
|
59
|
+
opt :output, "Output Epub filename", :default => 'out.epub'
|
60
|
+
opt :toc, "Add Table of Contents page", :default => true
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
# Returns a Hash, in which filenames are grouped by extname.
|
65
|
+
def read_files
|
66
|
+
Dir['*', '*/*'].group_by { |f| f.ext || :_dir }.to_symkey
|
67
|
+
end
|
68
|
+
|
69
|
+
# Returns a Hash of metadata.
|
70
|
+
def read_metadata(path)
|
71
|
+
unless path
|
72
|
+
abort "Must exist a markdown filename which have Yaml Front Matter."
|
73
|
+
end
|
74
|
+
yml, _ = Maliq::FileUtils.retrieveYFM(File.read path)
|
75
|
+
return nil if yml.empty?
|
76
|
+
YAML.load(yml).to_symkey
|
77
|
+
end
|
78
|
+
|
79
|
+
def generate(metadata, files, opts)
|
80
|
+
meta_nodes = GEPUB::Metadata::CONTENT_NODE_LIST + ["unique_identifier"]
|
81
|
+
metadata.select! { |k, _| meta_nodes.include? k.to_s }
|
82
|
+
csses = files.values_atx(:css)
|
83
|
+
images = files.values_atx(:png, :jpg, :jpeg, :gif, :bmp, :tiff)
|
84
|
+
cover_images, images = images.partition { |f| File.basename(f, '.*').match /^cover/ }
|
85
|
+
xhtmls = files.values_atx(:xhtml, :html)
|
86
|
+
cover_xhtml = xhtmls.delete('cover.xhtml')
|
87
|
+
|
88
|
+
# Create cover page if page not provided for a cover image.
|
89
|
+
if (cover = cover_images.first) && !cover_xhtml
|
90
|
+
cover_xhtml = create_cover_page(cover)
|
91
|
+
end
|
92
|
+
|
93
|
+
xhtmls.map! { |f| xhtml_with_heading f }
|
94
|
+
|
95
|
+
# Create Table of Contents page.
|
96
|
+
if opts[:toc] && no_nav_page?(xhtmls)
|
97
|
+
nav_xhtml = create_nav_page(xhtmls, css:csses)
|
98
|
+
end
|
99
|
+
|
100
|
+
GEPUB::Builder.new do
|
101
|
+
metadata.each { |k, v| send k, *Array(v) }
|
102
|
+
|
103
|
+
resources(:workdir => '.') {
|
104
|
+
csses.each { |f| file f }
|
105
|
+
cover_image cover
|
106
|
+
images.each { |f| file(f) }
|
107
|
+
ordered {
|
108
|
+
if cover_xhtml
|
109
|
+
file 'cover.xhtml' => cover_xhtml
|
110
|
+
heading 'Cover'
|
111
|
+
end
|
112
|
+
|
113
|
+
if nav_xhtml
|
114
|
+
file 'nav.xhtml' => nav_xhtml
|
115
|
+
heading 'Table of Contents'
|
116
|
+
end
|
117
|
+
|
118
|
+
xhtmls.each do |fname, head|
|
119
|
+
file fname
|
120
|
+
heading head
|
121
|
+
end
|
122
|
+
}
|
123
|
+
}
|
124
|
+
end.generate_epub(opts[:output])
|
125
|
+
end
|
126
|
+
|
127
|
+
def create_cover_page(cover)
|
128
|
+
out = Maliq::Converter.new("", title:'Cover').run
|
129
|
+
StringIO.new(out)
|
130
|
+
end
|
131
|
+
|
132
|
+
def no_nav_page?(xhtmls)
|
133
|
+
xhtmls.none? { |fname, _| File.basename(fname, '.*').match /(nav|toc)$/ }
|
134
|
+
end
|
135
|
+
|
136
|
+
def create_nav_page(xhtmls, opts={})
|
137
|
+
out = Maliq::Converter.new(~<<-EOS, opts).run
|
138
|
+
## Table of Contents
|
139
|
+
|
140
|
+
#{xhtmls.map { |fname, text| "1. [#{text}](#{fname})" }.join("\n")}
|
141
|
+
|
142
|
+
EOS
|
143
|
+
StringIO.new(out)
|
144
|
+
end
|
145
|
+
|
146
|
+
def xhtml_with_heading(xhtml)
|
147
|
+
heading = File.basename(xhtml, '.*').capitalize
|
148
|
+
File.read(xhtml).match(/<h(?:1|2|3)>(.*?)<\/h(?:1|2|3)>/) { heading = $1 }
|
149
|
+
[xhtml, heading]
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
OptionParser.parse!
|
data/lib/maliq.rb
ADDED
@@ -0,0 +1,85 @@
|
|
1
|
+
gem "rdiscount"
|
2
|
+
autoload :RDiscount, "rdiscount"
|
3
|
+
require "yaml"
|
4
|
+
require "liquid"
|
5
|
+
|
6
|
+
class Maliq::Converter
|
7
|
+
include Maliq::FileUtils
|
8
|
+
attr_reader :meta
|
9
|
+
def initialize(text, opts={})
|
10
|
+
@engine = ->text{ ::RDiscount.new(text).to_html }
|
11
|
+
@text = text
|
12
|
+
@converted = nil
|
13
|
+
@meta = { language:'ja', liquid:'plugins' }
|
14
|
+
retrieve_meta_from_yfm
|
15
|
+
set_meta(opts)
|
16
|
+
end
|
17
|
+
|
18
|
+
def run(template=:epub)
|
19
|
+
text = convert_liquid_tags(@text)
|
20
|
+
@converted = apply_template(template) { @engine.call(text) }
|
21
|
+
end
|
22
|
+
alias :to_xhtml :run
|
23
|
+
alias :convert :run
|
24
|
+
|
25
|
+
def save(path="out.xhtml")
|
26
|
+
@converted ||= run
|
27
|
+
File.write(path, @converted)
|
28
|
+
end
|
29
|
+
|
30
|
+
def set_meta(meta)
|
31
|
+
@meta.update(meta)
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
def retrieve_meta_from_yfm
|
36
|
+
yfm, @text = retrieveYFM(@text)
|
37
|
+
set_meta(YAML.load(yfm).to_symkey) unless yfm.empty?
|
38
|
+
end
|
39
|
+
|
40
|
+
def convert_liquid_tags(text)
|
41
|
+
if dir = meta[:liquid]
|
42
|
+
plugins = File.join(Dir.pwd, dir, '*.rb')
|
43
|
+
Dir[plugins].each { |lib| require lib }
|
44
|
+
end
|
45
|
+
Liquid::Template.parse(text).render
|
46
|
+
end
|
47
|
+
|
48
|
+
def apply_template(template, &blk)
|
49
|
+
case template
|
50
|
+
when nil, false then blk.call
|
51
|
+
when :epub then epub_template(&blk)
|
52
|
+
else raise "Only :epub template available so far."
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def epub_template(&blk)
|
57
|
+
header, footer = ->lang,title,css{ ~<<-HEAD }, ~<<-FOOT
|
58
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
59
|
+
<!DOCTYPE html>
|
60
|
+
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:epub="http://www.idpf.org/2007/ops" xml:lang="#{lang}">
|
61
|
+
<head>
|
62
|
+
<title>#{title}</title>
|
63
|
+
#{css}
|
64
|
+
</head>
|
65
|
+
<body>
|
66
|
+
HEAD
|
67
|
+
</body>
|
68
|
+
</html>
|
69
|
+
FOOT
|
70
|
+
|
71
|
+
lang, title = meta[:language], meta[:title]
|
72
|
+
css = css_link(meta[:css])
|
73
|
+
[header[lang, title, css], blk.call, footer].join
|
74
|
+
end
|
75
|
+
|
76
|
+
# TODO: indent problem
|
77
|
+
def css_link(css)
|
78
|
+
template = ->f{ "<link href='#{f}' rel='stylesheet' type='text/css'/>" }
|
79
|
+
case css
|
80
|
+
when String then template[css]
|
81
|
+
when Array then css.map { |f| template[f] }.join("\n")
|
82
|
+
else
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Maliq
|
2
|
+
module FileUtils
|
3
|
+
SPLIT_MARKER = /^<<<---\s*([\w\.]+?)\s*--->>>\n/
|
4
|
+
|
5
|
+
# Retrieve Yaml Front Matter from text.
|
6
|
+
# Returns [yfm, text]
|
7
|
+
def retrieveYFM(text)
|
8
|
+
yfm = ""
|
9
|
+
text.match(/^(---\s*\n.*?\n?)^(---\s*$\n?)/m) do |md|
|
10
|
+
yfm = md.to_s
|
11
|
+
text = md.post_match
|
12
|
+
end
|
13
|
+
return yfm, text
|
14
|
+
end
|
15
|
+
|
16
|
+
def split(path, marker=nil)
|
17
|
+
marker ||= SPLIT_MARKER
|
18
|
+
content = File.read(path)
|
19
|
+
filename = File.basename(path, '.*')
|
20
|
+
yfm, content = retrieveYFM(content)
|
21
|
+
contents = ([filename] + content.split(marker)).to_hash
|
22
|
+
contents.with({}) { |(fname, text), h| h[fname] = yfm + text }
|
23
|
+
end
|
24
|
+
|
25
|
+
module_function :split, :retrieveYFM
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
@@ -0,0 +1,34 @@
|
|
1
|
+
class String
|
2
|
+
def ~
|
3
|
+
margin = scan(/^ +/).map(&:size).min
|
4
|
+
gsub(/^ {#{margin}}/, '')
|
5
|
+
end
|
6
|
+
|
7
|
+
def basename_with(ext)
|
8
|
+
"#{File.basename(self, '.*')}.#{ext}"
|
9
|
+
end
|
10
|
+
|
11
|
+
def ext
|
12
|
+
File.extname(self)[/\w+$/]
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
class Hash
|
17
|
+
def to_symkey
|
18
|
+
with({}) { |(k, v), h| h[k.intern] = v }
|
19
|
+
end
|
20
|
+
|
21
|
+
def values_atx(*keys)
|
22
|
+
res = values_at(*keys)
|
23
|
+
res = res.flatten if res.respond_to?(:flatten)
|
24
|
+
res.compact
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
class Array
|
29
|
+
def to_hash
|
30
|
+
Hash[ *self ]
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
Enumerable.send(:alias_method, :with, :each_with_object)
|
data/maliq.gemspec
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'maliq/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |gem|
|
7
|
+
gem.name = "maliq"
|
8
|
+
gem.version = Maliq::VERSION
|
9
|
+
gem.authors = ["kyoendo"]
|
10
|
+
gem.email = ["postagie@gmail.com"]
|
11
|
+
gem.description = %q{Maliq is a markdown, liquid converter for EPUB's xhtml.}
|
12
|
+
gem.summary = %q{Maliq is a markdown, liquid converter for EPUB's xhtml. It comes with two command 'maliq' and 'maliq\_gepub'. 'maliq' is a markdown-xhtml converter and 'maliq\_gepub' is a wrapper of gepub gem which is a EPUB generator.}
|
13
|
+
gem.homepage = "https://github.com/melborne/maliq"
|
14
|
+
|
15
|
+
gem.files = `git ls-files`.split($/)
|
16
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
17
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
18
|
+
gem.require_paths = ["lib"]
|
19
|
+
gem.required_ruby_version = '>=1.9.3'
|
20
|
+
gem.add_dependency 'trollop'
|
21
|
+
gem.add_development_dependency 'rspec'
|
22
|
+
end
|
@@ -0,0 +1,132 @@
|
|
1
|
+
require_relative 'spec_helper'
|
2
|
+
|
3
|
+
describe Maliq::Converter do
|
4
|
+
let(:converter) { Maliq::Converter }
|
5
|
+
before(:each) do
|
6
|
+
@header, @footer = ->lang='ja',title=nil{ ~<<-HEAD }, ~<<-FOOT
|
7
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
8
|
+
<!DOCTYPE html>
|
9
|
+
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:epub="http://www.idpf.org/2007/ops" xml:lang="#{lang}">
|
10
|
+
<head>
|
11
|
+
<title>#{title}</title>
|
12
|
+
|
13
|
+
</head>
|
14
|
+
<body>
|
15
|
+
HEAD
|
16
|
+
</body>
|
17
|
+
</html>
|
18
|
+
FOOT
|
19
|
+
end
|
20
|
+
|
21
|
+
describe "#run" do
|
22
|
+
context "h1 header with some lines" do
|
23
|
+
subject { converter.new("#header1\nline1\n\nline2").run }
|
24
|
+
|
25
|
+
it { should eql [@header.call, ~<<-EOS, @footer].join }
|
26
|
+
<h1>header1</h1>
|
27
|
+
|
28
|
+
<p>line1</p>
|
29
|
+
|
30
|
+
<p>line2</p>
|
31
|
+
EOS
|
32
|
+
end
|
33
|
+
|
34
|
+
context "without template" do
|
35
|
+
subject { converter.new("#header1\nline1\n\nline2").run(false) }
|
36
|
+
|
37
|
+
it { should eql ~<<-EOS }
|
38
|
+
<h1>header1</h1>
|
39
|
+
|
40
|
+
<p>line1</p>
|
41
|
+
|
42
|
+
<p>line2</p>
|
43
|
+
EOS
|
44
|
+
end
|
45
|
+
|
46
|
+
context "with YAML front matter" do
|
47
|
+
subject { converter.new(~<<-EOS).run }
|
48
|
+
---
|
49
|
+
title: 'Title of Book'
|
50
|
+
language: 'en'
|
51
|
+
---
|
52
|
+
# header1
|
53
|
+
EOS
|
54
|
+
it { should eql [@header['en','Title of Book'], ~<<-EOS, @footer].join }
|
55
|
+
<h1>header1</h1>
|
56
|
+
EOS
|
57
|
+
end
|
58
|
+
|
59
|
+
context "with Liquid tags" do
|
60
|
+
subject { converter.new(~<<-EOS).run }
|
61
|
+
# header1
|
62
|
+
Hello {{ 'tobi' | upcase }}
|
63
|
+
EOS
|
64
|
+
it { should eql [@header.call, ~<<-EOS, @footer].join }
|
65
|
+
<h1>header1</h1>
|
66
|
+
|
67
|
+
<p>Hello TOBI</p>
|
68
|
+
EOS
|
69
|
+
end
|
70
|
+
|
71
|
+
context "with Liquid plugins" do
|
72
|
+
subject { converter.new(~<<-EOS).run }
|
73
|
+
---
|
74
|
+
liquid: 'spec/plugins'
|
75
|
+
---
|
76
|
+
# header1
|
77
|
+
{% calc 2 + 3 %}
|
78
|
+
EOS
|
79
|
+
it { should eql [@header.call, ~<<-EOS, @footer].join }
|
80
|
+
<h1>header1</h1>
|
81
|
+
|
82
|
+
<p>2 + 3 = 5</p>
|
83
|
+
EOS
|
84
|
+
end
|
85
|
+
|
86
|
+
context "with css links" do
|
87
|
+
before(:each) do
|
88
|
+
@header, @footer = ~<<-HEAD, ~<<-FOOT
|
89
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
90
|
+
<!DOCTYPE html>
|
91
|
+
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:epub="http://www.idpf.org/2007/ops" xml:lang="ja">
|
92
|
+
<head>
|
93
|
+
<title></title>
|
94
|
+
<link href='style.css' rel='stylesheet' type='text/css'/>
|
95
|
+
<link href='syntax.css' rel='stylesheet' type='text/css'/>
|
96
|
+
</head>
|
97
|
+
<body>
|
98
|
+
HEAD
|
99
|
+
</body>
|
100
|
+
</html>
|
101
|
+
FOOT
|
102
|
+
end
|
103
|
+
|
104
|
+
subject { converter.new("#header1\nline1\n\nline2", css:['style.css', 'syntax.css']).run }
|
105
|
+
|
106
|
+
it { should eql [@header, ~<<-EOS, @footer].join }
|
107
|
+
<h1>header1</h1>
|
108
|
+
|
109
|
+
<p>line1</p>
|
110
|
+
|
111
|
+
<p>line2</p>
|
112
|
+
EOS
|
113
|
+
end
|
114
|
+
|
115
|
+
end
|
116
|
+
|
117
|
+
describe "#save" do
|
118
|
+
it "save to a file" do
|
119
|
+
Dir.mktmpdir do |dir|
|
120
|
+
tmpfile = "#{dir}/tmp"
|
121
|
+
converter.new("#header1\nline1\n\nline2").save(tmpfile)
|
122
|
+
File.read(tmpfile).should eql [@header.call, ~<<-EOS, @footer].join
|
123
|
+
<h1>header1</h1>
|
124
|
+
|
125
|
+
<p>line1</p>
|
126
|
+
|
127
|
+
<p>line2</p>
|
128
|
+
EOS
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
require_relative 'spec_helper'
|
2
|
+
|
3
|
+
include Maliq::FileUtils
|
4
|
+
|
5
|
+
describe Maliq::FileUtils do
|
6
|
+
describe ".split" do
|
7
|
+
context "without chapters and front matter" do
|
8
|
+
before do
|
9
|
+
Dir.mktmpdir do |dir|
|
10
|
+
tmpf = "#{dir}/tmp"
|
11
|
+
@content = ~<<-EOF
|
12
|
+
#hello
|
13
|
+
hello
|
14
|
+
#Goodbye
|
15
|
+
goodbye
|
16
|
+
#Yo
|
17
|
+
yoyoyo
|
18
|
+
EOF
|
19
|
+
File.write(tmpf, @content)
|
20
|
+
@f = split(tmpf)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
it { @f.should == {'tmp' => @content} }
|
25
|
+
end
|
26
|
+
|
27
|
+
context "with chapters" do
|
28
|
+
before do
|
29
|
+
Dir.mktmpdir do |dir|
|
30
|
+
tmpf = "#{dir}/tmp"
|
31
|
+
@content = ~<<-EOF
|
32
|
+
#hello
|
33
|
+
hello
|
34
|
+
<<<--- chapter02 --->>>
|
35
|
+
#Goodbye
|
36
|
+
goodbye
|
37
|
+
<<<--- chapter03 --->>>
|
38
|
+
#Yo
|
39
|
+
yoyoyo
|
40
|
+
EOF
|
41
|
+
@ch1, @ch2, @ch3 = ~<<-F1, ~<<-F2, ~<<-F3
|
42
|
+
#hello
|
43
|
+
hello
|
44
|
+
F1
|
45
|
+
#Goodbye
|
46
|
+
goodbye
|
47
|
+
F2
|
48
|
+
#Yo
|
49
|
+
yoyoyo
|
50
|
+
F3
|
51
|
+
File.write(tmpf, @content)
|
52
|
+
@f = split(tmpf)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
it { @f.should == {'tmp' => @ch1, 'chapter02' => @ch2, 'chapter03' => @ch3} }
|
57
|
+
end
|
58
|
+
|
59
|
+
context "with chapters and front matter" do
|
60
|
+
before do
|
61
|
+
Dir.mktmpdir do |dir|
|
62
|
+
tmpf = "#{dir}/tmp"
|
63
|
+
@content = ~<<-EOF
|
64
|
+
---
|
65
|
+
title: Helo, Friends
|
66
|
+
---
|
67
|
+
#hello
|
68
|
+
hello
|
69
|
+
<<<--- chapter02 --->>>
|
70
|
+
#Goodbye
|
71
|
+
goodbye
|
72
|
+
<<<--- chapter03 --->>>
|
73
|
+
#Yo
|
74
|
+
yoyoyo
|
75
|
+
EOF
|
76
|
+
@ch1, @ch2, @ch3 = ~<<-F1, ~<<-F2, ~<<-F3
|
77
|
+
---
|
78
|
+
title: Helo, Friends
|
79
|
+
---
|
80
|
+
#hello
|
81
|
+
hello
|
82
|
+
F1
|
83
|
+
---
|
84
|
+
title: Helo, Friends
|
85
|
+
---
|
86
|
+
#Goodbye
|
87
|
+
goodbye
|
88
|
+
F2
|
89
|
+
---
|
90
|
+
title: Helo, Friends
|
91
|
+
---
|
92
|
+
#Yo
|
93
|
+
yoyoyo
|
94
|
+
F3
|
95
|
+
File.write(tmpf, @content)
|
96
|
+
@f = split(tmpf)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
it { @f.should == {'tmp' => @ch1, 'chapter02' => @ch2, 'chapter03' => @ch3} }
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
class CalcTag < Liquid::Tag
|
2
|
+
def initialize(tag_name, text, token)
|
3
|
+
super
|
4
|
+
@text = text
|
5
|
+
end
|
6
|
+
|
7
|
+
def render(context)
|
8
|
+
if exp = @text.match(/[\s\d\(\)+\*\/-]+/) { $& }
|
9
|
+
"#{exp}= #{eval(exp)}"
|
10
|
+
else
|
11
|
+
""
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
Liquid::Template.register_tag('calc', CalcTag)
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,102 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: maliq
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- kyoendo
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-11-29 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: trollop
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
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: '0'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: rspec
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0'
|
38
|
+
type: :development
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
46
|
+
description: Maliq is a markdown, liquid converter for EPUB's xhtml.
|
47
|
+
email:
|
48
|
+
- postagie@gmail.com
|
49
|
+
executables:
|
50
|
+
- maliq
|
51
|
+
- maliq_gepub
|
52
|
+
extensions: []
|
53
|
+
extra_rdoc_files: []
|
54
|
+
files:
|
55
|
+
- .gitignore
|
56
|
+
- Gemfile
|
57
|
+
- LICENSE.txt
|
58
|
+
- README.md
|
59
|
+
- Rakefile
|
60
|
+
- bin/maliq
|
61
|
+
- bin/maliq_gepub
|
62
|
+
- lib/maliq.rb
|
63
|
+
- lib/maliq/converter.rb
|
64
|
+
- lib/maliq/file_utils.rb
|
65
|
+
- lib/maliq/system_extensions.rb
|
66
|
+
- lib/maliq/version.rb
|
67
|
+
- maliq.gemspec
|
68
|
+
- spec/converter_spec.rb
|
69
|
+
- spec/file_utils_spec.rb
|
70
|
+
- spec/plugins/calc.rb
|
71
|
+
- spec/spec_helper.rb
|
72
|
+
homepage: https://github.com/melborne/maliq
|
73
|
+
licenses: []
|
74
|
+
post_install_message:
|
75
|
+
rdoc_options: []
|
76
|
+
require_paths:
|
77
|
+
- lib
|
78
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
79
|
+
none: false
|
80
|
+
requirements:
|
81
|
+
- - ! '>='
|
82
|
+
- !ruby/object:Gem::Version
|
83
|
+
version: 1.9.3
|
84
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
85
|
+
none: false
|
86
|
+
requirements:
|
87
|
+
- - ! '>='
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
requirements: []
|
91
|
+
rubyforge_project:
|
92
|
+
rubygems_version: 1.8.23
|
93
|
+
signing_key:
|
94
|
+
specification_version: 3
|
95
|
+
summary: Maliq is a markdown, liquid converter for EPUB's xhtml. It comes with two
|
96
|
+
command 'maliq' and 'maliq\_gepub'. 'maliq' is a markdown-xhtml converter and 'maliq\_gepub'
|
97
|
+
is a wrapper of gepub gem which is a EPUB generator.
|
98
|
+
test_files:
|
99
|
+
- spec/converter_spec.rb
|
100
|
+
- spec/file_utils_spec.rb
|
101
|
+
- spec/plugins/calc.rb
|
102
|
+
- spec/spec_helper.rb
|