rakali 0.0.8
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.
- checksums.yaml +7 -0
- data/bin/rakali +5 -0
- data/lib/rakali.rb +22 -0
- data/lib/rakali/cli.rb +23 -0
- data/lib/rakali/converter.rb +63 -0
- data/lib/rakali/document.rb +95 -0
- data/lib/rakali/logger.rb +92 -0
- data/lib/rakali/utils.rb +29 -0
- data/lib/rakali/version.rb +3 -0
- metadata +164 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 5a9db33f283b3c0c005337c9f4e278198b72f5a0
|
4
|
+
data.tar.gz: dd9193ea8f7ffedff4bf346554ec5901a282f69a
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 9a35a109180245835fecac87d95a514483ca9975b2757ed7aa32c92f904135da4eb7298680c86c1445fe465e44490741c3f81b4f5f2577b873477ed434ec6173
|
7
|
+
data.tar.gz: ee50a5a11353568443edafdbb5255b753b018e54520f362457790c3b7a98dfa3e845ca45a792647d016c3848382806058bde03bae6485c115d0f52f2bdd5f95c
|
data/bin/rakali
ADDED
data/lib/rakali.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
require "thor"
|
2
|
+
require 'safe_yaml/load'
|
3
|
+
require 'colorator'
|
4
|
+
require 'json-schema'
|
5
|
+
require 'json'
|
6
|
+
require 'open3'
|
7
|
+
|
8
|
+
require 'rakali/version'
|
9
|
+
require 'rakali/cli'
|
10
|
+
require 'rakali/converter'
|
11
|
+
require 'rakali/document'
|
12
|
+
require 'rakali/utils'
|
13
|
+
require 'rakali/logger'
|
14
|
+
|
15
|
+
SafeYAML::OPTIONS[:suppress_warnings] = false
|
16
|
+
SafeYAML::OPTIONS[:default_mode] = :safe
|
17
|
+
|
18
|
+
module Rakali
|
19
|
+
def self.logger
|
20
|
+
@logger ||= Logger.new
|
21
|
+
end
|
22
|
+
end
|
data/lib/rakali/cli.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
module Rakali
|
4
|
+
class CLI < Thor
|
5
|
+
def self.exit_on_failure?
|
6
|
+
true
|
7
|
+
end
|
8
|
+
|
9
|
+
desc "read FILE", "read configuration FILE in yaml format"
|
10
|
+
|
11
|
+
def convert(file)
|
12
|
+
converter = Rakali::Converter.new(file)
|
13
|
+
|
14
|
+
# don't proceed unless input validates against schema
|
15
|
+
unless converter.valid?
|
16
|
+
message = "Error: input did not validate with schema #{converter.schema["title"]}\n" + converter.errors.join("\n")
|
17
|
+
raise Thor::Error, message
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
default_task :convert
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
module Rakali
|
4
|
+
class Converter
|
5
|
+
|
6
|
+
# Default options.
|
7
|
+
DEFAULTS = {
|
8
|
+
'from' => { 'format' => 'md' },
|
9
|
+
'to' => { 'folder' => nil, 'format' => 'html' },
|
10
|
+
'schema' => 'schemata/default.json',
|
11
|
+
'strict' => false,
|
12
|
+
'templates' => 'rakali/templates',
|
13
|
+
'csl' => 'rakali/csl',
|
14
|
+
'bibliography' => 'rakali/bibliography',
|
15
|
+
'filters' => 'rakali/filters',
|
16
|
+
'writers' => 'rakali/writers'
|
17
|
+
}
|
18
|
+
|
19
|
+
attr_accessor :config, :documents, :errors
|
20
|
+
|
21
|
+
def initialize(file, options = {})
|
22
|
+
begin
|
23
|
+
config = read_config_file(file)
|
24
|
+
|
25
|
+
# deep merge defaults to preserve nested keys
|
26
|
+
@config = Utils.deep_merge_hashes(DEFAULTS, config)
|
27
|
+
|
28
|
+
# print configuration
|
29
|
+
Rakali.logger.info "Starting:", "Reading configuration... \n#{to_yaml}"
|
30
|
+
|
31
|
+
from_folder = @config.fetch('from').fetch('folder')
|
32
|
+
from_format = @config.fetch('from').fetch('format')
|
33
|
+
documents = Dir.glob("#{from_folder}/*.#{from_format}")
|
34
|
+
@documents = documents.map { |document| Rakali::Document.new(document, @config) }
|
35
|
+
rescue KeyError => e
|
36
|
+
Rakali.logger.abort_with "Fatal:", "Configuration #{e.message}."
|
37
|
+
rescue => e
|
38
|
+
Rakali.logger.abort_with "Fatal:", "#{e.message}."
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def read_config_file(file)
|
43
|
+
# use an empty hash if the file is empty
|
44
|
+
SafeYAML.load_file(file) || {}
|
45
|
+
rescue SystemCallError
|
46
|
+
Rakali.logger.abort_with "Fatal:", "Configuration file not found: \"#{file}\"."
|
47
|
+
end
|
48
|
+
|
49
|
+
def from_json(string)
|
50
|
+
JSON.parse(string)
|
51
|
+
rescue JSON::ParserError, TypeError
|
52
|
+
nil
|
53
|
+
end
|
54
|
+
|
55
|
+
def to_json
|
56
|
+
content.to_json
|
57
|
+
end
|
58
|
+
|
59
|
+
def to_yaml
|
60
|
+
config.to_yaml
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
module Rakali
|
4
|
+
class Document
|
5
|
+
|
6
|
+
attr_accessor :config, :source, :destination, :content, :schema, :errors, :to_folder
|
7
|
+
|
8
|
+
def initialize(document, config)
|
9
|
+
begin
|
10
|
+
@config = config
|
11
|
+
|
12
|
+
@from_folder = @config.fetch('from').fetch('folder')
|
13
|
+
@from_format = @config.fetch('from').fetch('format')
|
14
|
+
@to_folder = @config.fetch('to').fetch('folder') || @from_folder
|
15
|
+
@to_format = @config.fetch('to').fetch('format')
|
16
|
+
#config_path = File.expand_path("../config.yml", __FILE__)
|
17
|
+
|
18
|
+
# for destination filename use source name with new extension
|
19
|
+
@source = File.basename(document)
|
20
|
+
@destination = @source.sub(/\.#{@from_format}$/, ".#{@to_format}")
|
21
|
+
|
22
|
+
# convert source document into JSON version of native AST
|
23
|
+
@content = convert(nil, "#{@from_folder}/#{@source} -t json")
|
24
|
+
|
25
|
+
# read in JSON schema
|
26
|
+
@schema = IO.read(@config.fetch('schema'))
|
27
|
+
|
28
|
+
# validate JSON against schema and report errors
|
29
|
+
@errors = validate
|
30
|
+
|
31
|
+
# convert to destination document from JSON version of native AST
|
32
|
+
@output = convert(@content, "-f json -o #{@to_folder}/#{@destination}")
|
33
|
+
Rakali.logger.abort_with "Fatal:", "Writing file #{@destination} failed" unless created?
|
34
|
+
|
35
|
+
if @errors.empty?
|
36
|
+
Rakali.logger.info "Success:", "Converted file #{@source} to file #{@destination}."
|
37
|
+
else
|
38
|
+
Rakali.logger.warn "With Errors:", "Converted file #{@source} to file #{@destination}."
|
39
|
+
end
|
40
|
+
rescue KeyError => e
|
41
|
+
Rakali.logger.abort_with "Fatal:", "Configuration #{e.message}."
|
42
|
+
rescue => e
|
43
|
+
Rakali.logger.abort_with "Fatal:", "#{e.message}."
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def convert(string = nil, args)
|
48
|
+
Open3::popen3("pandoc #{args}") do |stdin, stdout, stderr, wait_thr|
|
49
|
+
unless string.nil?
|
50
|
+
stdin.puts string
|
51
|
+
stdin.close
|
52
|
+
end
|
53
|
+
|
54
|
+
# abort with log message if exit_status of command not 0
|
55
|
+
Rakali.logger.abort_with "Fatal:", "#{stderr.read}." if wait_thr.value.exitstatus > 0
|
56
|
+
|
57
|
+
stdout.read
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def validate
|
62
|
+
errors = JSON::Validator.fully_validate(@schema, @content)
|
63
|
+
return [] if errors.empty?
|
64
|
+
|
65
|
+
if @config.fetch('strict', false)
|
66
|
+
errors.each { |error| Rakali.logger.error "Validation Error:", "#{error} for file #{source}" }
|
67
|
+
Rakali.logger.abort_with "Fatal:", "Conversion of file #{source} failed."
|
68
|
+
else
|
69
|
+
errors.each { |error| Rakali.logger.warn "Validation Error:", "#{error} for file #{source}" }
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def valid?
|
74
|
+
errors == []
|
75
|
+
end
|
76
|
+
|
77
|
+
def created?
|
78
|
+
# file exists
|
79
|
+
return false unless File.exist?("#{@to_folder}/#{@destination}")
|
80
|
+
|
81
|
+
# file was created in the last 5 seconds
|
82
|
+
Time.now - File.mtime("#{@to_folder}/#{@destination}") < 5
|
83
|
+
end
|
84
|
+
|
85
|
+
def from_json(string)
|
86
|
+
JSON.parse(string)
|
87
|
+
rescue JSON::ParserError, TypeError
|
88
|
+
nil
|
89
|
+
end
|
90
|
+
|
91
|
+
def to_json
|
92
|
+
content.to_json
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
# This code was taken from Jekyll, available under MIT-LICENSE
|
2
|
+
# Copyright (c) 2008-2014 Tom Preston-Werner
|
3
|
+
|
4
|
+
module Rakali
|
5
|
+
class Logger
|
6
|
+
attr_accessor :log_level
|
7
|
+
|
8
|
+
DEBUG = 0
|
9
|
+
INFO = 1
|
10
|
+
WARN = 2
|
11
|
+
ERROR = 3
|
12
|
+
|
13
|
+
# Public: Create a new logger instance
|
14
|
+
#
|
15
|
+
# level - (optional, integer) the log level
|
16
|
+
#
|
17
|
+
# Returns nothing
|
18
|
+
def initialize(level = INFO)
|
19
|
+
@log_level = level
|
20
|
+
end
|
21
|
+
|
22
|
+
# Public: Print a debug message to stdout
|
23
|
+
#
|
24
|
+
# topic - the topic of the message, e.g. "Configuration file", "Deprecation", etc.
|
25
|
+
# message - the message detail
|
26
|
+
#
|
27
|
+
# Returns nothing
|
28
|
+
def debug(topic, message = nil)
|
29
|
+
$stdout.puts(message(topic, message)) if log_level <= DEBUG
|
30
|
+
end
|
31
|
+
|
32
|
+
# Public: Print a message to stdout
|
33
|
+
#
|
34
|
+
# topic - the topic of the message, e.g. "Configuration file", "Deprecation", etc.
|
35
|
+
# message - the message detail
|
36
|
+
#
|
37
|
+
# Returns nothing
|
38
|
+
def info(topic, message = nil)
|
39
|
+
$stdout.puts(message(topic, message)) if log_level <= INFO
|
40
|
+
end
|
41
|
+
|
42
|
+
# Public: Print a message to stderr
|
43
|
+
#
|
44
|
+
# topic - the topic of the message, e.g. "Configuration file", "Deprecation", etc.
|
45
|
+
# message - the message detail
|
46
|
+
#
|
47
|
+
# Returns nothing
|
48
|
+
def warn(topic, message = nil)
|
49
|
+
$stderr.puts(message(topic, message).yellow) if log_level <= WARN
|
50
|
+
end
|
51
|
+
|
52
|
+
# Public: Print a error message to stderr
|
53
|
+
#
|
54
|
+
# topic - the topic of the message, e.g. "Configuration file", "Deprecation", etc.
|
55
|
+
# message - the message detail
|
56
|
+
#
|
57
|
+
# Returns nothing
|
58
|
+
def error(topic, message = nil)
|
59
|
+
$stderr.puts(message(topic, message).red) if log_level <= ERROR
|
60
|
+
end
|
61
|
+
|
62
|
+
# Public: Print a error message to stderr and immediately abort the process
|
63
|
+
#
|
64
|
+
# topic - the topic of the message, e.g. "Configuration file", "Deprecation", etc.
|
65
|
+
# message - the message detail (can be omitted)
|
66
|
+
#
|
67
|
+
# Returns nothing
|
68
|
+
def abort_with(topic, message = nil)
|
69
|
+
error(topic, message)
|
70
|
+
abort
|
71
|
+
end
|
72
|
+
|
73
|
+
# Public: Build a topic method
|
74
|
+
#
|
75
|
+
# topic - the topic of the message, e.g. "Configuration file", "Deprecation", etc.
|
76
|
+
# message - the message detail
|
77
|
+
#
|
78
|
+
# Returns the formatted message
|
79
|
+
def message(topic, message)
|
80
|
+
formatted_topic(topic) + message.to_s.gsub(/---\n/, '')
|
81
|
+
end
|
82
|
+
|
83
|
+
# Public: Format the topic
|
84
|
+
#
|
85
|
+
# topic - the topic of the message, e.g. "Configuration file", "Deprecation", etc.
|
86
|
+
#
|
87
|
+
# Returns the formatted topic statement
|
88
|
+
def formatted_topic(topic)
|
89
|
+
"#{topic} ".rjust(20)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
data/lib/rakali/utils.rb
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
module Rakali
|
4
|
+
class Utils
|
5
|
+
# This code was taken from Jekyll, available under MIT-LICENSE
|
6
|
+
# Copyright (c) 2008-2014 Tom Preston-Werner
|
7
|
+
# Merges a master hash with another hash, recursively.
|
8
|
+
#
|
9
|
+
# master_hash - the "parent" hash whose values will be overridden
|
10
|
+
# other_hash - the other hash whose values will be persisted after the merge
|
11
|
+
#
|
12
|
+
# This code was lovingly stolen from some random gem:
|
13
|
+
# http://gemjack.com/gems/tartan-0.1.1/classes/Hash.html
|
14
|
+
def self.deep_merge_hashes(master_hash, other_hash)
|
15
|
+
target = master_hash.dup
|
16
|
+
|
17
|
+
other_hash.keys.each do |key|
|
18
|
+
if other_hash[key].is_a? Hash and target[key].is_a? Hash
|
19
|
+
target[key] = deep_merge_hashes(target[key], other_hash[key])
|
20
|
+
next
|
21
|
+
end
|
22
|
+
|
23
|
+
target[key] = other_hash[key]
|
24
|
+
end
|
25
|
+
|
26
|
+
target
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
metadata
ADDED
@@ -0,0 +1,164 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: rakali
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.8
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Martin Fenner
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-08-17 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: thor
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0.19'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0.19'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: json-schema
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '2.2'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '2.2'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: safe_yaml
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '1.0'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '1.0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: colorator
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0.1'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0.1'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rake
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: rspec
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '2.6'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '2.6'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: cucumber
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - "~>"
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '1.3'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - "~>"
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '1.3'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: aruba
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - "~>"
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - "~>"
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '0'
|
125
|
+
description: A Pandoc command-line wrapper written in Ruby.
|
126
|
+
email: mf@martinfenner.org
|
127
|
+
executables:
|
128
|
+
- rakali
|
129
|
+
extensions: []
|
130
|
+
extra_rdoc_files: []
|
131
|
+
files:
|
132
|
+
- bin/rakali
|
133
|
+
- lib/rakali.rb
|
134
|
+
- lib/rakali/cli.rb
|
135
|
+
- lib/rakali/converter.rb
|
136
|
+
- lib/rakali/document.rb
|
137
|
+
- lib/rakali/logger.rb
|
138
|
+
- lib/rakali/utils.rb
|
139
|
+
- lib/rakali/version.rb
|
140
|
+
homepage: https://github.com/rakali/rakali.rb
|
141
|
+
licenses:
|
142
|
+
- MIT
|
143
|
+
metadata: {}
|
144
|
+
post_install_message:
|
145
|
+
rdoc_options: []
|
146
|
+
require_paths:
|
147
|
+
- lib
|
148
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
149
|
+
requirements:
|
150
|
+
- - ">="
|
151
|
+
- !ruby/object:Gem::Version
|
152
|
+
version: '0'
|
153
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
154
|
+
requirements:
|
155
|
+
- - ">="
|
156
|
+
- !ruby/object:Gem::Version
|
157
|
+
version: 1.3.6
|
158
|
+
requirements: []
|
159
|
+
rubyforge_project:
|
160
|
+
rubygems_version: 2.2.2
|
161
|
+
signing_key:
|
162
|
+
specification_version: 4
|
163
|
+
summary: A Pandoc command-line wrapper
|
164
|
+
test_files: []
|