neruda 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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 0f5fb5997c152378f937f4b226182001790d4c35
4
+ data.tar.gz: 5fd12f03ac8acdab7a6f26e03226a0dba2a39ee6
5
+ SHA512:
6
+ metadata.gz: dc0c38d5bfce0fd5c82e39fe0aedc5596a15e4e38a0d2b0fc400959708fc23b22f4feb5fa96691a0059e034791529f4ff927e21dde2559e1a82ca65daf2b2fd5
7
+ data.tar.gz: 81112c53025ca25ebcd6a64927455556948c486d0a05d624d1d8c502a54174dc7f5d70d8e0badf7fa3ffaf64eab67c3f385de3cd85d858fe1b8b46fc9dec467e
data/LICENSE ADDED
@@ -0,0 +1,14 @@
1
+ DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
2
+ Version 2, December 2004
3
+
4
+ Copyright (C) 2004 Sam Hocevar <sam@hocevar.net>
5
+
6
+ Everyone is permitted to copy and distribute verbatim or modified
7
+ copies of this license document, and changing it is allowed as long
8
+ as the name is changed.
9
+
10
+ DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
11
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
12
+
13
+ 0. You just DO WHAT THE FUCK YOU WANT TO.
14
+
data/README.org ADDED
@@ -0,0 +1,56 @@
1
+ #+title: Neruda
2
+
3
+ *Neruda* is a little [[http://sinatrarb.com][Sinatra]] app, which aims to offer a quick access for
4
+ writers to web publication. It is named in memory of Pablo Neruda.
5
+
6
+ * Install
7
+
8
+ These installation instructions use [[https://rvm.io][rvm]] as ruby provider. If you want to
9
+ use rbenv or your system ruby, please refer to their specific
10
+ documentation to know how to install ruby 2.4 and [[https://bundler.io/][bundler]].
11
+
12
+ First we need to upgrade rvm (always do that from time to time):
13
+
14
+ #+begin_src shell
15
+ $ rvm get latest
16
+ $ rvm install ruby-2.4.1
17
+ #+end_src
18
+
19
+ Then the installation itself:
20
+
21
+ #+begin_src shell
22
+ $ mkdir mysite
23
+ $ rvm use ruby-2.4.1
24
+ $ rvm gemset create neruda
25
+ $ rvm gemset use neruda
26
+ $ gem install neruda
27
+ #+end_src
28
+
29
+ Finally, you need to configure it to your needs.
30
+
31
+ #+begin_src shell
32
+ $ pablo init
33
+ #+end_src
34
+
35
+ * Running it
36
+
37
+ For testing it, you should just run:
38
+
39
+ #+begin_src shell
40
+ $ pablo start
41
+ #+end_src
42
+
43
+ For production use, just pass the right environnement variable:
44
+
45
+ #+begin_src shell
46
+ $ APP_ENV=production pablo start
47
+ #+end_src
48
+
49
+ To stop it, just enter =ctrl+C= if you are in development mode or enter
50
+ =pablo stop= in production mode.
51
+
52
+ You can now start your first chapter: =pablo new=
53
+
54
+ * Known issues
55
+
56
+ See [[TODO.org][TODO.org]]
data/TODO.org ADDED
@@ -0,0 +1,12 @@
1
+ #+title: TODO
2
+
3
+ * TODO Documentation
4
+
5
+ Write a better documentation
6
+
7
+ * TODO Capistrano tasks
8
+
9
+ ** TODO Add capistrano commands to =pablo=
10
+ ** TODO Complete documentation on capistrano
11
+
12
+ * TODO Deploy a sample website
data/bin/pablo ADDED
@@ -0,0 +1,151 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'yaml'
4
+ require 'fileutils'
5
+
6
+ def help
7
+ unless File.exist? 'config/config.yml'
8
+ STDERR.puts 'Please init your website: pablo init'
9
+ exit 1
10
+ end
11
+
12
+ options = ['start', 'stop', 'new']
13
+ options << 'capify' if File.exist? 'Capfile'
14
+
15
+ STDERR.puts "pablo [ #{options.join(' | ')} ]"
16
+ exit 1
17
+ end
18
+
19
+ def capify
20
+ unless File.exist? 'Capfile'
21
+ STDERR.puts 'Capfile does not exist. Aborting'
22
+ exit 1
23
+ end
24
+
25
+ capfile = File.new('Capfile', 'a')
26
+ capfile.write <<EOF
27
+ neruda_spec = Gem::Specification.find_by_name 'neruda'
28
+ Dir.glob("\#{neruda_spec.gem_dir}/lib/tasks/*.rake").each { |r| import r }
29
+ Dir.glob("\#{neruda_spec.gem_dir}/lib/tasks/capistrano/*.rake").each { |r| import r }
30
+ EOF
31
+ end
32
+
33
+ def _init_path
34
+ FileUtils.mkdir_p 'config'
35
+ FileUtils.mkdir_p 'public'
36
+ FileUtils.mkdir_p 'tmp/pids'
37
+ FileUtils.mkdir_p 'private/orgs'
38
+ FileUtils.mkdir_p 'private/epubs'
39
+ end
40
+
41
+ def _init_config
42
+ config = {}
43
+ print 'Name of your website: '
44
+ config['title'] = STDIN.gets.strip
45
+ print 'Your author name: '
46
+ config['author'] = STDIN.gets.strip
47
+ print 'Main language of your website [en_US]: '
48
+ lang = STDIN.gets.strip
49
+ lang = 'en_US' if lang == ''
50
+ config['lang'] = lang
51
+
52
+ main_title = config['title'].tr(' ', '_').downcase.gsub(/[^a-z0-9_]/, '')
53
+ print "Filename of the main generated epub [#{main_title}]: "
54
+ epub = STDIN.gets.strip
55
+ if epub == ''
56
+ epub = main_title
57
+ else
58
+ epub = File.basename(epub, '.epub')
59
+ end
60
+ config['book_filename'] = epub
61
+ config['chapters'] = []
62
+
63
+ IO.write 'config/config.yml', config.to_yaml
64
+ end
65
+
66
+ def _init_assets
67
+ unless Dir.exist? 'views'
68
+ FileUtils.cp_r File.join(__dir__, '../lib/assets'), 'views'
69
+ end
70
+
71
+ return if File.exist? 'public/style.css'
72
+ FileUtils.mv 'views/style.css', 'public/style.css'
73
+ end
74
+
75
+ def _init_rackup
76
+ unless File.exist? 'Rakefile'
77
+ FileUtils.copy File.join(__dir__, '../docs/Rakefile.example'), 'Rakefile'
78
+ end
79
+
80
+ return if File.exist? 'config.ru'
81
+ rackup_conf = <<EOF
82
+ # frozen_string_literal: true
83
+
84
+ require 'neruda'
85
+ run Neruda::App
86
+ EOF
87
+ IO.write('config.ru', rackup_conf)
88
+ end
89
+
90
+ def init
91
+ _init_path
92
+ _init_config
93
+ _init_assets
94
+ _init_rackup
95
+ end
96
+
97
+ def create_new(title)
98
+ if title == ''
99
+ filename = 'new'
100
+ else
101
+ filename = title.tr(' ', '_').downcase.gsub(/[^a-z0-9_]/, '')
102
+ end
103
+
104
+ filename = "private/orgs/#{filename}.org"
105
+
106
+ if Dir.exist? 'private/orgs'
107
+ config = YAML.load_file('config/config.yml')
108
+ IO.write filename, <<EOF
109
+ #+title: #{title}
110
+ #+date: <#{Date.today.strftime('%Y-%m-%d %a.')}>
111
+ #+author: #{config['author']}
112
+
113
+
114
+ EOF
115
+ end
116
+
117
+ editor = ENV['EDITOR'] || 'emacs'
118
+ exec editor, filename
119
+ end
120
+
121
+ if ARGV[0] == 'init'
122
+ init
123
+
124
+ elsif ARGV[0] == 'capify'
125
+ capify
126
+
127
+ elsif ['start', 'run'].include?(ARGV[0])
128
+ puts 'You can also start neruda with the following command:'
129
+ puts 'rake sinatra:start'
130
+
131
+ exec 'rake', 'sinatra:start'
132
+
133
+ elsif ARGV[0] == 'stop'
134
+ puts 'You can also stop neruda with the following command:'
135
+ puts 'rake sinatra:stop'
136
+
137
+ exec 'rake', 'sinatra:stop'
138
+
139
+ elsif ARGV[0] == 'new'
140
+ if ARGV[1].nil?
141
+ print 'Title: '
142
+ title = STDIN.gets.strip
143
+ else
144
+ title = ARGV[1]
145
+ end
146
+
147
+ create_new title
148
+
149
+ else
150
+ help
151
+ end
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+
3
+ neruda_spec = Gem::Specification.find_by_name 'neruda'
4
+ Dir.glob("#{neruda_spec.gem_dir}/lib/tasks/*.rake").each { |r| import r }
@@ -0,0 +1,17 @@
1
+ ---
2
+
3
+ title: My Awesome Title
4
+ author: Me Myself
5
+ # license: © Me
6
+ # lang: en_US
7
+
8
+ book_filename: my_awesome_title
9
+
10
+ ## If we are behind a subfolder reverse proxy
11
+ # base_path: /subfolder
12
+
13
+ chapters:
14
+ - in_the
15
+ - order
16
+ - we_wish
17
+ - they_appear
@@ -0,0 +1,14 @@
1
+ .chapter
2
+ h1.chapter-title = title
3
+
4
+ .chapter-meta
5
+ p.chapter-info
6
+ span.chapter-date = meta_data 'date'
7
+ br
8
+ span.chapter-epub
9
+ a href=proxy_url("/epub/#{@slug}") epub version
10
+ ' -
11
+ span.chapter-permalink
12
+ a href=proxy_url("/chapter/#{@slug}") permalink
13
+
14
+ == @content.to_html
@@ -0,0 +1,13 @@
1
+ h1 = title
2
+
3
+ == text
4
+
5
+ p
6
+ a href=proxy_url('/epub/all') Télécharger la version complète
7
+
8
+ h2 Chapitres
9
+
10
+ ul
11
+ - @chapters.each do |c|
12
+ li
13
+ a href=proxy_url("/chapter/#{c[:slug]}") = c[:title]
@@ -0,0 +1,17 @@
1
+ doctype html
2
+ html
3
+ head
4
+ title #{title}
5
+ meta name='author' content=author
6
+ link rel='stylesheet' href=proxy_url('/style.css') type='text/css'
7
+
8
+ body
9
+ #content
10
+ == yield
11
+
12
+ footer#footer.status
13
+ p © #{author}
14
+ p#gotop
15
+ a href=proxy_url('/') Accueil
16
+ ' ·
17
+ a href='#top' Aller en haut
@@ -0,0 +1,199 @@
1
+ body {
2
+ font-family: serif;
3
+ font-size: 20pt;
4
+ }
5
+
6
+ a:hover {
7
+ text-decoration: none;
8
+ }
9
+
10
+ dt {
11
+ font-weight: bold;
12
+ }
13
+ dt:after {
14
+ font-weight: bold;
15
+ content: ":";
16
+ }
17
+
18
+ figure {
19
+ text-align: center;
20
+ }
21
+
22
+ kbd {
23
+ border-width: .3em;
24
+ border-style: solid;
25
+ }
26
+
27
+ #content,
28
+ #footer,
29
+ .status {
30
+ width: 968px;
31
+ margin: 1em auto;
32
+ }
33
+
34
+ #content img {
35
+ max-width: 100%;
36
+ }
37
+
38
+
39
+ /**
40
+ * Content
41
+ */
42
+
43
+ #content {
44
+ padding: 1em;
45
+ line-height: 1.5em;
46
+ }
47
+
48
+ #content h1.chapter-title {
49
+ margin: 0;
50
+ line-height: 1.5em;
51
+ }
52
+
53
+ .chapter-meta {
54
+ font-size: smaller;
55
+ padding: 0 .4em;
56
+ border-left-style: solid;
57
+ border-left-width: .4em;
58
+ border-radius: .4em;
59
+ }
60
+ .chapter-meta p {
61
+ margin: .3em 0;
62
+ }
63
+
64
+ .src {
65
+ font-family: Inconsolata, monospace;
66
+ font-size: 1em;
67
+ display:block;
68
+ padding-left:.4em;
69
+ margin:1em 1.5em;
70
+ line-height:1.5em;
71
+ border-left-width: .4em;
72
+ border-left-style: solid;
73
+ border-radius: .4em;
74
+ overflow-x: auto;
75
+ }
76
+
77
+ blockquote {
78
+ display: block;
79
+ padding: 0 1.5em;
80
+ font-style: italic;
81
+ margin: 1em 0;
82
+ }
83
+ blockquote::after {
84
+ display: table;
85
+ content: "";
86
+ clear: both;
87
+ }
88
+ blockquote>*:first-child::before {
89
+ content: "« ";
90
+ font-size: 3em;
91
+ }
92
+ blockquote>*:last-child::after {
93
+ content: " »";
94
+ font-size: 2em;
95
+ }
96
+
97
+ div.footnotes,
98
+ .footdef {
99
+ font-size: smaller;
100
+ }
101
+ div.footnotes::before,
102
+ .footdef::before {
103
+ content: "---";
104
+ display: block;
105
+ }
106
+ div.footnotes,
107
+ .footdef sup {
108
+ font-size: smaller;
109
+ }
110
+
111
+ #content .chapter::after {
112
+ content: "❦";
113
+ font-size: xx-large;
114
+ display: block;
115
+ text-align: center;
116
+ }
117
+
118
+ /**
119
+ * Footer
120
+ */
121
+ #gotop {
122
+ position: fixed;
123
+ bottom: 0em;
124
+ right: 1em;
125
+ font-size: smaller;
126
+ }
127
+
128
+ #footer {
129
+ clear: both;
130
+ padding-top: 0;
131
+ font-size: smaller;
132
+ margin-bottom: .5em;
133
+ }
134
+
135
+ /**
136
+ * Responsive...
137
+ */
138
+ @media screen and (max-width: 968px) {
139
+ #content, .status {
140
+ width: 90%;
141
+ }
142
+ }
143
+
144
+
145
+ /**
146
+ * Colors
147
+ * We used the Dracula color theme
148
+ * https://draculatheme.com/
149
+ */
150
+
151
+ body {
152
+ background: #282a36;
153
+ color: #f8f8f2;
154
+ }
155
+
156
+ h1 {
157
+ color: #ffb86c;
158
+ }
159
+
160
+ h2 {
161
+ color: #bd93f9;
162
+ }
163
+
164
+ a:link, a:visited {
165
+ color: #ff79c6;
166
+ }
167
+
168
+ kbd {
169
+ border-color: #f8f8f2;
170
+ }
171
+
172
+ code {
173
+ background: #373844;
174
+ color: #e2e2dc;
175
+ }
176
+
177
+ mark {
178
+ background: #f1fa8c;
179
+ }
180
+
181
+ .chapter-meta {
182
+ border-left-color: #373844;
183
+ background-color: #464752;
184
+ }
185
+
186
+ .src {
187
+ border-left-color: #bd93f9;
188
+ }
189
+
190
+ blockquote>*:first-child::before {
191
+ color: #50fa7b;
192
+ }
193
+ blockquote>*:last-child::after {
194
+ color: #50fa7b;
195
+ }
196
+
197
+ .comment-content {
198
+ border-left-color: #bd93f9;
199
+ }
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Various method to handle org conversion and metadata access
4
+ module Neruda::Chapter
5
+ def meta_data(key)
6
+ return nil if @content.nil?
7
+ value = @content.in_buffer_settings[key.upcase]
8
+ return value if value.nil? || !key.casecmp('date').zero?
9
+ time_for(value).strftime('%A %d %B %Y')
10
+ end
11
+
12
+ def title
13
+ return Neruda::CONFIG['title'] if @content.nil?
14
+ return @title unless @title.nil?
15
+ # We use an instance variable to avoid Orgmode::Parser to render
16
+ # title with the rest of the file
17
+ # Thus we are removing title from the buffer_settings
18
+ @title = @content.in_buffer_settings.delete('TITLE')
19
+ return @title unless @title.nil?
20
+ @title = Neruda::CONFIG['title']
21
+ end
22
+
23
+ def author
24
+ meta_data('author') || Neruda::CONFIG['author']
25
+ end
26
+ end
data/lib/neruda/url.rb ADDED
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'uri'
4
+
5
+ # Various method to ease url handling
6
+ module Neruda::Url
7
+ def proxy_url(url)
8
+ wanted_uri = url(url)
9
+ return wanted_uri if Neruda::CONFIG['base_path'].nil?
10
+ uri_data = URI.parse(wanted_uri)
11
+ uri_data.path = Neruda::CONFIG['base_path'] + uri_data.path
12
+ uri_data.to_s
13
+ end
14
+ end
data/lib/neruda.rb ADDED
@@ -0,0 +1,96 @@
1
+ # coding: utf-8
2
+ # frozen_string_literal: true
3
+
4
+ require 'yaml'
5
+ require 'org-ruby'
6
+ require 'sinatra/base'
7
+
8
+ # Namespacing
9
+ module Neruda
10
+ CONFIG = YAML.load_file(File.join('config', 'config.yml')).freeze
11
+ end
12
+
13
+ # The following must be required after to allow compact name style
14
+ require 'neruda/url'
15
+ require 'neruda/chapter'
16
+
17
+ # Main Sinatra application
18
+ class Neruda::App < Sinatra::Base
19
+ configure :production, :development do
20
+ # When used as a Gem, we lose the correct path.
21
+ set :root, Dir.pwd
22
+ enable :logging
23
+ disable :method_override, :sessions
24
+ mime_type :epub, 'application/epub+zip'
25
+ end
26
+
27
+ include Neruda::Url
28
+ include Neruda::Chapter
29
+
30
+ def find_slug
31
+ @slug = params[:chapter]
32
+ halt 404 if @slug.nil?
33
+ end
34
+
35
+ def find_file(kind = 'org')
36
+ f = File.join('private', "#{kind}s", "#{@slug}.#{kind}")
37
+ halt 404 unless File.exist? f
38
+ f
39
+ end
40
+
41
+ def find_chapter
42
+ @org_file = find_file
43
+ end
44
+
45
+ get '/epub/:chapter' do
46
+ find_slug
47
+ if @slug == 'all' && !Neruda::CONFIG['book_filename'].nil?
48
+ @slug = Neruda::CONFIG['book_filename']
49
+ end
50
+ epub_file = find_file('epub')
51
+ halt 404 unless File.exist? epub_file
52
+ content_type :epub
53
+ send_file epub_file, filename: "#{@slug}.epub"
54
+ end
55
+
56
+ get '/chapter/:chapter' do
57
+ find_slug
58
+ find_chapter
59
+ @content = Orgmode::Parser.load @org_file
60
+ title # Force the early removal of the title
61
+ slim :chapter
62
+ end
63
+
64
+ unless Neruda::CONFIG['base_path'].nil?
65
+ # If we are behind a misconfigured subfolder reverse proxy, this one
66
+ # could be usefull
67
+ get Neruda::CONFIG['base_path'] do
68
+ call env.merge('PATH_INFO' => '/')
69
+ end
70
+ end
71
+
72
+ get '/' do
73
+ @slug = 'index'
74
+ text_content = ''
75
+ if File.exist? File.join('private', 'orgs', 'index.org')
76
+ find_chapter
77
+ @content = Orgmode::Parser.load @org_file
78
+ text_content = @content.to_html
79
+ end
80
+
81
+ @chapters = []
82
+ if File.exist? File.join('config', 'chapters.yml')
83
+ @chapters = YAML.load_file(File.join('config', 'chapters.yml'))
84
+ end
85
+
86
+ slim :index, locals: { text: text_content }
87
+ end
88
+
89
+ error 403 do
90
+ slim 'h1 Access forbidden'
91
+ end
92
+
93
+ error 404 do
94
+ slim 'h1 Not Found'
95
+ end
96
+ end
@@ -0,0 +1,49 @@
1
+ require 'yaml'
2
+
3
+ namespace :chapters do
4
+
5
+ desc 'Upload epub files listed in tmp/epub_to_upload.yml'
6
+ task :upload_epubs do
7
+ next unless File.exist?('tmp/epub_to_upload.yml')
8
+ epub_to_upload = YAML.load_file('tmp/epub_to_upload.yml')
9
+ next if epub_to_upload.empty?
10
+ on roles(:app) do
11
+ epub_to_upload.each do |f|
12
+ upload! f, "#{shared_path}/#{f}"
13
+ end
14
+ end
15
+ end
16
+
17
+ desc 'Upload the chapters list index'
18
+ task upload_index: 'chapters:index' do
19
+ on roles(:app) do
20
+ upload! 'config/chapters.yml', "#{release_path}/config/chapters.yml"
21
+ info 'chatpers.yml has been built and uploaded'
22
+ end
23
+ end
24
+
25
+ desc 'Upload the complete book'
26
+ task upload_book: ['chapters:make_book', 'chapters:upload_epubs']
27
+
28
+ namespace :purge do
29
+ desc 'Remove remote orphaned epub files'
30
+ task :remote do
31
+ neruda_config = YAML.load_file('config/config.yml')
32
+ final_org = neruda_config['book_filename'] || 'all'
33
+ on roles(:app) do
34
+ within release_path do
35
+ epub_files = capture :ls, '-1', 'private/epubs/*.epub'
36
+ epub_files.each_line do |filename|
37
+ filename.delete!("\n")
38
+ file_radix = File.basename(filename, '.epub')
39
+ next if file_radix == final_org
40
+ org_file = "private/orgs/#{file_radix}.org"
41
+ unless test("[ -e '#{release_path}/#{org_file}' ]")
42
+ execute :rm, filename
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,16 @@
1
+ namespace :sinatra do
2
+ namespace :restart do
3
+ desc 'Restart the remote neruda application'
4
+ task :remote do
5
+ on roles(:app) do
6
+ within release_path do
7
+ if test("[ -e '#{release_path}/tmp/pids/sinatra.pid' ]")
8
+ execute :pkill, '-F', 'tmp/pids/sinatra.pid'
9
+ end
10
+ execute :bundle, :exec, :rackup, '-E', fetch(:app_env),
11
+ '-P', 'tmp/pids/sinatra.pid', '-D'
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,135 @@
1
+ require 'yaml'
2
+
3
+ namespace :chapters do
4
+ desc 'Identify all org files'
5
+ task :list_all_orgs do
6
+ org_to_convert = []
7
+ File.unlink 'tmp/org_to_convert.yml' if File.exist? 'tmp/org_to_convert.yml'
8
+ neruda_config = YAML.load_file('config/config.yml')
9
+ chapters = neruda_config['chapters']
10
+ next unless chapters.any?
11
+ chapters.each do |file_radix|
12
+ next if file_radix == 'index'
13
+ filename = "private/orgs/#{file_radix}.org"
14
+ org_to_convert << filename
15
+ end
16
+ next if org_to_convert.empty?
17
+ Dir.mkdir 'tmp' unless Dir.exist? 'tmp'
18
+ IO.write('tmp/org_to_convert.yml', org_to_convert.to_yaml)
19
+ end
20
+
21
+ desc 'Identify orphan (without epubs) org files'
22
+ task list_orphaned_orgs: 'chapters:list_all_orgs' do
23
+ next unless File.exist? 'tmp/org_to_convert.yml'
24
+ org_to_convert = []
25
+ chapters = YAML.load_file('tmp/org_to_convert.yml')
26
+ next unless chapters.any?
27
+ chapters.each do |filename|
28
+ file_radix = File.basename(filename, '.org')
29
+ epub_file = "private/epubs/#{file_radix}.epub"
30
+ org_to_convert << filename unless File.exist? epub_file
31
+ end
32
+ next if org_to_convert.empty?
33
+ IO.write('tmp/org_to_convert.yml', org_to_convert.to_yaml)
34
+ end
35
+
36
+ desc 'Convert org files from tmp/org_to_convert.yml to epubs'
37
+ task :convert_org do
38
+ next unless File.exist?('tmp/org_to_convert.yml')
39
+ File.unlink 'tmp/epub_to_upload.yml' if File.exist? 'tmp/epub_to_upload.yml'
40
+ org_to_convert = YAML.load_file('tmp/org_to_convert.yml')
41
+ next if org_to_convert.empty?
42
+ epub_to_upload = []
43
+ org_to_convert.each do |filename|
44
+ file_radix = File.basename(filename, '.org')
45
+ next if file_radix == 'index'
46
+ epub_file = "private/epubs/#{file_radix}.epub"
47
+ epub_to_upload << epub_file
48
+ execute :pandoc, '-S', "-o #{epub_file}", filename
49
+ end
50
+ next if epub_to_upload.empty?
51
+ IO.write('tmp/epub_to_upload.yml', epub_to_upload.to_yaml)
52
+ end
53
+
54
+ desc 'Build missing epubs'
55
+ task build_epubs: ['chapters:list_orphaned_orgs', 'chapters:convert_org']
56
+
57
+ namespace :build_epubs do
58
+ desc '(re)Build all epub files for each org files'
59
+ task force_all: ['chapters:list_all_orgs', 'chapters:convert_org']
60
+ end
61
+
62
+ desc 'Remove orphaned (epub without org files) epub files'
63
+ task :purge do
64
+ neruda_config = YAML.load_file('config/config.yml')
65
+ final_org = neruda_config['book_filename'] || 'all'
66
+ chapters = neruda_config['chapters']
67
+ Dir.glob('private/orgs/*.org') do |filename|
68
+ file_radix = File.basename(filename, '.org')
69
+ unless chapters.include? file_radix
70
+ STDERR.puts "WARNING: #{filename} exists but #{file_radix} " \
71
+ 'is no more in the chapters list.'
72
+ end
73
+ end
74
+
75
+ Dir.glob('private/epubs/*.epub') do |filename|
76
+ file_radix = File.basename(filename, '.epub')
77
+ next if file_radix == final_org
78
+ org_file = "private/orgs/#{file_radix}.org"
79
+ File.unlink filename unless File.exist? org_file
80
+ end
81
+ end
82
+
83
+ desc 'Create/Update the chapters list index'
84
+ task :index do
85
+ require 'org-ruby'
86
+ neruda_config = YAML.load_file('config/config.yml')
87
+ next if neruda_config['chapters'].nil?
88
+ chapters = []
89
+ neruda_config['chapters'].each do |file_radix|
90
+ filename = "private/orgs/#{file_radix}.org"
91
+ next unless File.exist? filename
92
+ f = Orgmode::Parser.load filename
93
+ title = f.in_buffer_settings['TITLE']
94
+ chapters << { slug: file_radix, title: title } unless title.nil?
95
+ end
96
+ IO.write('config/chapters.yml', chapters.to_yaml)
97
+ end
98
+
99
+ desc 'Create the org file of the complete book'
100
+ task prepare_book: 'chapters:index' do
101
+ chapters = YAML.load_file('config/chapters.yml')
102
+ next if chapters.nil?
103
+
104
+ neruda_config = YAML.load_file('config/config.yml')
105
+ final_org = neruda_config['book_filename'] || 'all'
106
+ final_org = "tmp/#{final_org}.org"
107
+
108
+ Dir.mkdir 'tmp' unless Dir.exist? 'tmp'
109
+ File.unlink final_org if File.exist? final_org
110
+
111
+ org_file = File.open(final_org, 'a')
112
+ org_file.write("#+title: #{neruda_config['title']}\n")
113
+ org_file.write("#+author: #{neruda_config['author']}\n")
114
+ org_file.write("#+rights: #{neruda_config['license']}\n")
115
+ org_file.write("#+language: #{neruda_config['lang']}\n\n")
116
+
117
+ chapters.each do |c|
118
+ file_radix = c[:slug]
119
+ filename = "private/orgs/#{file_radix}.org"
120
+ next unless File.exist? filename
121
+ file_content = IO.read(filename)
122
+ file_content.gsub!(/^#\+date:.*$/mi, '')
123
+ file_content.gsub!(/^#\+author:.*$/mi, '')
124
+ file_content.gsub!(/^(\*+)\s+(.*)$/mi, '*\1 \2')
125
+ file_content.gsub!(/^#\+title:\s?(.*)$/mi, '* \1')
126
+
127
+ org_file.write(file_content + "\n")
128
+ end
129
+ org_file.close
130
+
131
+ IO.write('tmp/org_to_convert.yml', [final_org].to_yaml)
132
+ end
133
+
134
+ task make_book: ['chapters:prepare_book', 'chapters:convert_org']
135
+ end
@@ -0,0 +1,23 @@
1
+ namespace :sinatra do
2
+ desc 'Stop the underlaying sinatra application'
3
+ task :stop do
4
+ unless File.exist? 'tmp/pids/neruda.pid'
5
+ STDERR.puts 'No pid file found'
6
+ exit 1
7
+ end
8
+ pid = IO.read('tmp/pids/neruda.pid').strip.to_i
9
+ Process.kill('TERM', pid)
10
+ File.unlink 'tmp/pids/neruda.pid'
11
+ end
12
+
13
+ desc 'Start the underlaying sinatra application'
14
+ task :start do
15
+ loc_env = ENV['APP_ENV'] || 'development'
16
+ cmd = ['rackup', "-E #{loc_env}", '-P', 'tmp/pids/neruda.pid']
17
+ cmd << '-D' if loc_env == 'production'
18
+ exec cmd.join(' ')
19
+ end
20
+
21
+ desc 'Restart local sinatra server'
22
+ task restart: ['sinatra:stop', 'sinatra:start']
23
+ end
metadata ADDED
@@ -0,0 +1,117 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: neruda
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Étienne Deparis
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2017-06-12 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: org-ruby
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '0.9'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '0.9'
27
+ - !ruby/object:Gem::Dependency
28
+ name: slim
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '3.0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '3.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: thin
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '1.7'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '1.7'
55
+ - !ruby/object:Gem::Dependency
56
+ name: sinatra
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '2.0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '2.0'
69
+ description: Write your org files, we take care of the rest.
70
+ email: etienne@depar.is
71
+ executables:
72
+ - pablo
73
+ extensions: []
74
+ extra_rdoc_files: []
75
+ files:
76
+ - LICENSE
77
+ - README.org
78
+ - TODO.org
79
+ - bin/pablo
80
+ - docs/Rakefile.example
81
+ - docs/config.yml.example
82
+ - lib/assets/chapter.slim
83
+ - lib/assets/index.slim
84
+ - lib/assets/layout.slim
85
+ - lib/assets/style.css
86
+ - lib/neruda.rb
87
+ - lib/neruda/chapter.rb
88
+ - lib/neruda/url.rb
89
+ - lib/tasks/capistrano/chapters.rake
90
+ - lib/tasks/capistrano/sinatra.rake
91
+ - lib/tasks/chapters.rake
92
+ - lib/tasks/sinatra.rake
93
+ homepage: https://git.deparis.io/neruda/
94
+ licenses:
95
+ - WTFPL
96
+ metadata: {}
97
+ post_install_message:
98
+ rdoc_options: []
99
+ require_paths:
100
+ - lib
101
+ required_ruby_version: !ruby/object:Gem::Requirement
102
+ requirements:
103
+ - - ">="
104
+ - !ruby/object:Gem::Version
105
+ version: '0'
106
+ required_rubygems_version: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ requirements: []
112
+ rubyforge_project:
113
+ rubygems_version: 2.6.12
114
+ signing_key:
115
+ specification_version: 4
116
+ summary: A simplistic way to publish a book online.
117
+ test_files: []