neruda 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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: []