runeblog 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 +7 -0
- data/README.lt3 +35 -0
- data/README.md +35 -0
- data/bin/blog +58 -0
- data/lib/runeblog.rb +294 -0
- data/runeblog.gemspec +26 -0
- metadata +49 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 9973f7b46eac607aa4f682ba37ac617a8e66e331
|
4
|
+
data.tar.gz: b117cbd903331a914bff1be70bb617271de3ccc0
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: be32ea81007a5d708380b1025a00ff0b2310df8177bab6d3f06db567397248196ca67d76e0c0c67e4a77271ec144a133504f1318698a64cbbd33f76e27687154
|
7
|
+
data.tar.gz: 9f07259eb728b822d02a6916ee292994b5f2fc5b60cd7081ac68b49a7bd33513bfb73d4f8b9ade2568e81bcd71e7ec77a2ea8991676eebe5f07ae35443c0d70e
|
data/README.lt3
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
runeblog
|
2
|
+
--------
|
3
|
+
Runeblog is a blogging tool written in Ruby. It has these basic characteristics:
|
4
|
+
- It is usable entirely from the command line
|
5
|
+
- It publishes web pages as static HTML
|
6
|
+
- So far, yes, like Jekyll
|
7
|
+
- It's based on Livetext (highly extensible Ruby-based markup)
|
8
|
+
- It has the concept of multiple "views" for a blog
|
9
|
+
|
10
|
+
What is Livetext?
|
11
|
+
-----------------
|
12
|
+
Livetext is a markup format that is a throwback to the old, old days of text
|
13
|
+
formatters such as nroff. It is very flexible, and it is extensible _in Ruby_.
|
14
|
+
It is not yet full-featured, but it is usable. Runeblog uses Livetext, along
|
15
|
+
with some custom definitions, to try to ease the formatting of a blog entry.
|
16
|
+
|
17
|
+
What are "views"?
|
18
|
+
-----------------
|
19
|
+
Think of them as multiple separate blogs with the same backend. Why would you
|
20
|
+
want to do this? Maybe you wouldn't, but I do.
|
21
|
+
|
22
|
+
The purpose is to present different "faces" to different audiences. In my case,
|
23
|
+
my computing-related stuff would go into one view, and my hometown-related things
|
24
|
+
would go into another. There might be a view that only old friends or close friends
|
25
|
+
can see. There might be a view purely for reviews of music, books, and movies.
|
26
|
+
|
27
|
+
But the important points are these:
|
28
|
+
- _All_ the views will be managed the same way in the same place, and they will all share common data.
|
29
|
+
- Any post can easily be included in a single view, in more than one, or in all of them.
|
30
|
+
- Each view can have its own look and feel, and it can be linked/published separately from the others.
|
31
|
+
|
32
|
+
More later...
|
33
|
+
-------------
|
34
|
+
To be continued
|
35
|
+
|
data/README.md
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
runeblog
|
2
|
+
--------
|
3
|
+
Runeblog is a blogging tool written in Ruby. It has these basic characteristics:
|
4
|
+
- It is usable entirely from the command line
|
5
|
+
- It publishes web pages as static HTML
|
6
|
+
- So far, yes, like Jekyll
|
7
|
+
- It's based on Livetext (highly extensible Ruby-based markup)
|
8
|
+
- It has the concept of multiple "views" for a blog
|
9
|
+
|
10
|
+
What is Livetext?
|
11
|
+
-----------------
|
12
|
+
Livetext is a markup format that is a throwback to the old, old days of text
|
13
|
+
formatters such as nroff. It is very flexible, and it is extensible _in Ruby_.
|
14
|
+
It is not yet full-featured, but it is usable. Runeblog uses Livetext, along
|
15
|
+
with some custom definitions, to try to ease the formatting of a blog entry.
|
16
|
+
|
17
|
+
What are "views"?
|
18
|
+
-----------------
|
19
|
+
Think of them as multiple separate blogs with the same backend. Why would you
|
20
|
+
want to do this? Maybe you wouldn't, but I do.
|
21
|
+
|
22
|
+
The purpose is to present different "faces" to different audiences. In my case,
|
23
|
+
my computing-related stuff would go into one view, and my hometown-related things
|
24
|
+
would go into another. There might be a view that only old friends or close friends
|
25
|
+
can see. There might be a view purely for reviews of music, books, and movies.
|
26
|
+
|
27
|
+
But the important points are these:
|
28
|
+
- _All_ the views will be managed the same way in the same place, and they will all share common data.
|
29
|
+
- Any post can easily be included in a single view, in more than one, or in all of them.
|
30
|
+
- Each view can have its own look and feel, and it can be linked/published separately from the others.
|
31
|
+
|
32
|
+
More later...
|
33
|
+
-------------
|
34
|
+
To be continued
|
35
|
+
|
data/bin/blog
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
$: << "./lib"
|
4
|
+
|
5
|
+
require 'runeblog'
|
6
|
+
|
7
|
+
def execute_command
|
8
|
+
case @cmd
|
9
|
+
when "h", "help"; help
|
10
|
+
when "q", "quit"; exit
|
11
|
+
when "p", "post"; new_post
|
12
|
+
when "new post"; new_post # same as above
|
13
|
+
when "new view"; new_view(@arg)
|
14
|
+
when "lsv", "list views"; list_views
|
15
|
+
when "import post"; import(@arg)
|
16
|
+
when "relink"; relink
|
17
|
+
when "rebuild"; rebuild
|
18
|
+
else
|
19
|
+
puts "Huh? I don't know #{@cmd}"
|
20
|
+
end
|
21
|
+
puts
|
22
|
+
end
|
23
|
+
|
24
|
+
def help
|
25
|
+
puts <<-EOS
|
26
|
+
Commands:
|
27
|
+
h, help This message
|
28
|
+
q, quit Exit the program
|
29
|
+
p, post Create a new post
|
30
|
+
new post Same as post (create a post)
|
31
|
+
list views List all views available
|
32
|
+
lsv Same as: list views
|
33
|
+
new view Create a new view
|
34
|
+
relink Regenerate index for all views
|
35
|
+
rebuild Regenerate all posts and relink
|
36
|
+
EOS
|
37
|
+
end
|
38
|
+
|
39
|
+
###########
|
40
|
+
|
41
|
+
STDOUT.sync = true
|
42
|
+
|
43
|
+
@cmd = ARGV[0..1].join(" ")
|
44
|
+
@arg = ARGV[2]
|
45
|
+
|
46
|
+
if @cmd.empty? # REPL
|
47
|
+
read_config
|
48
|
+
loop do
|
49
|
+
print "blog> "
|
50
|
+
@cmd = gets.chomp.gsub(/ +/, " ") rescue abort("\n")
|
51
|
+
execute_command
|
52
|
+
end
|
53
|
+
else # one command
|
54
|
+
read_config
|
55
|
+
file = File.open("/dev/tty")
|
56
|
+
STDIN.reopen(file) # avoid ARGF dumbness
|
57
|
+
execute_command
|
58
|
+
end
|
data/lib/runeblog.rb
ADDED
@@ -0,0 +1,294 @@
|
|
1
|
+
|
2
|
+
class RuneBlog
|
3
|
+
VERSION = "0.0.1"
|
4
|
+
end
|
5
|
+
|
6
|
+
# FIXME lots of structure changes
|
7
|
+
|
8
|
+
require 'yaml'
|
9
|
+
|
10
|
+
=begin
|
11
|
+
|
12
|
+
Post
|
13
|
+
----
|
14
|
+
Create a blog post
|
15
|
+
Process it
|
16
|
+
Link it
|
17
|
+
Upload to server
|
18
|
+
|
19
|
+
|
20
|
+
data
|
21
|
+
views
|
22
|
+
computing
|
23
|
+
compiled
|
24
|
+
custom
|
25
|
+
deployment
|
26
|
+
|
27
|
+
=end
|
28
|
+
|
29
|
+
require 'rubygems'
|
30
|
+
require 'ostruct'
|
31
|
+
require 'livetext'
|
32
|
+
|
33
|
+
|
34
|
+
### ask
|
35
|
+
|
36
|
+
def ask(prompt, meth = :to_s)
|
37
|
+
print prompt
|
38
|
+
STDOUT.flush
|
39
|
+
STDIN.gets.chomp.send(meth)
|
40
|
+
end
|
41
|
+
|
42
|
+
### new_blog!
|
43
|
+
|
44
|
+
def new_blog!
|
45
|
+
unless File.exist?(".blog")
|
46
|
+
yn = ask("No .blog found. Create new blog?")
|
47
|
+
if yn.upcase == "Y"
|
48
|
+
system("mkdir data")
|
49
|
+
File.open(".blog", "w") {|f| f.puts data }
|
50
|
+
File.open("data/sequence", "w") {|f| f.puts 0 }
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
### next_sequence
|
56
|
+
|
57
|
+
def next_sequence
|
58
|
+
@config.sequence += 1
|
59
|
+
File.open("data/sequence", "w") {|f| f.puts @config.sequence }
|
60
|
+
@config.sequence
|
61
|
+
end
|
62
|
+
|
63
|
+
### make_slug
|
64
|
+
|
65
|
+
def make_slug(title, seq=nil)
|
66
|
+
num = '%04d' % (seq || next_sequence) # FIXME can do better
|
67
|
+
slug = title.downcase.strip.gsub(' ', '-').gsub(/[^\w-]/, '')
|
68
|
+
"#{num}-#{slug}"
|
69
|
+
end
|
70
|
+
|
71
|
+
### read_config
|
72
|
+
|
73
|
+
def read_config
|
74
|
+
@config = OpenStruct.new
|
75
|
+
# What views are there? Deployment, etc.
|
76
|
+
# Crude - FIXME later
|
77
|
+
root = File.readlines(".blog").first.chomp rescue "myblog"
|
78
|
+
dirs = Dir.entries("#{root}/views/") - %w[. ..]
|
79
|
+
dirs.reject! {|x| ! File.directory?("#{root}/views/#{x}") }
|
80
|
+
@config.root = root
|
81
|
+
@config.views = dirs
|
82
|
+
@config.sequence = File.read(root + "/sequence").to_i
|
83
|
+
end
|
84
|
+
|
85
|
+
### create_empty_post
|
86
|
+
|
87
|
+
def create_empty_post
|
88
|
+
@template = <<-EOS
|
89
|
+
.mixin liveblog
|
90
|
+
|
91
|
+
.liveblog_version
|
92
|
+
|
93
|
+
.title #{@title}
|
94
|
+
.pubdate #{@date}
|
95
|
+
.categories elixir ruby
|
96
|
+
.views computing
|
97
|
+
|
98
|
+
Teaser goes here.
|
99
|
+
.readmore
|
100
|
+
Remainder of post goes here.
|
101
|
+
EOS
|
102
|
+
|
103
|
+
@slug = make_slug(@title)
|
104
|
+
@fname = @slug + ".lt3"
|
105
|
+
File.open("#{@config.root}/src/#{@fname}", "w") {|f| f.puts @template }
|
106
|
+
@fname
|
107
|
+
end
|
108
|
+
|
109
|
+
### edit_post
|
110
|
+
|
111
|
+
def edit_post(file)
|
112
|
+
system("vi #{@config.root}/src/#{file}")
|
113
|
+
end
|
114
|
+
|
115
|
+
### process_post
|
116
|
+
|
117
|
+
def process_post(file)
|
118
|
+
lt ||= Livetext.new
|
119
|
+
puts " Processing: #{file}"
|
120
|
+
lt.process_file(file)
|
121
|
+
@meta = lt.main.instance_eval { @meta }
|
122
|
+
@meta.slug = file.sub(/.lt3$/, "")
|
123
|
+
@meta
|
124
|
+
end
|
125
|
+
|
126
|
+
### reload_post
|
127
|
+
|
128
|
+
def reload_post(file)
|
129
|
+
@main ||= Livetext.new
|
130
|
+
@meta = process_post("#{@config.root}/src/#{file}")
|
131
|
+
@meta.slug = file.sub(/.lt3$/, "")
|
132
|
+
@meta
|
133
|
+
end
|
134
|
+
|
135
|
+
### posting
|
136
|
+
|
137
|
+
def posting(meta)
|
138
|
+
<<-HTML
|
139
|
+
<br>
|
140
|
+
<font size=+1>#{meta.pubdate} </font>
|
141
|
+
<font size=+2 color=blue><a href=../#{"FAKEREF"} style="text-decoration: none">#{meta.title}</font></a>
|
142
|
+
<br>
|
143
|
+
#{meta.teaser}
|
144
|
+
<a href=../#{"FAKEREF2"} style="text-decoration: none">Read more...</a>
|
145
|
+
<br><br>
|
146
|
+
<hr>
|
147
|
+
HTML
|
148
|
+
end
|
149
|
+
|
150
|
+
### generate_index
|
151
|
+
|
152
|
+
def generate_index(view)
|
153
|
+
# Gather all posts, create list
|
154
|
+
vdir = "#{@config.root}/views/#{view}"
|
155
|
+
posts = Dir.entries(vdir).grep /^\d\d\d\d/
|
156
|
+
posts = posts.sort.reverse
|
157
|
+
# Add view header/trailer
|
158
|
+
@bloghead = File.read("#{vdir}/custom/blogheader.html") rescue ""
|
159
|
+
@blogtail = File.read("#{vdir}/custom/blogtrailer.html") rescue ""
|
160
|
+
# Output view
|
161
|
+
posts.map! do |post|
|
162
|
+
YAML.load(File.read("#{vdir}/#{post}/metadata.yaml"))
|
163
|
+
end
|
164
|
+
out = @bloghead.dup
|
165
|
+
posts.each do |post|
|
166
|
+
out << posting(post)
|
167
|
+
end
|
168
|
+
out << @blogtail
|
169
|
+
File.open("#{vdir}/index.html", "w") do |f|
|
170
|
+
f.puts out
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
### link_post_view
|
175
|
+
|
176
|
+
def link_post_view(view)
|
177
|
+
# Create dir using slug (index.html, metadata?)
|
178
|
+
vdir = "#{@config.root}/views/#{view}"
|
179
|
+
dir = "#{vdir}/#{@meta.slug}"
|
180
|
+
cmd = "mkdir -p #{dir}"
|
181
|
+
puts " Running: #{cmd}"
|
182
|
+
system(cmd)
|
183
|
+
File.write("#{dir}/metadata.yaml", @meta.to_yaml)
|
184
|
+
# Add header/trailer to post index
|
185
|
+
@posthead ||= File.read("#{vdir}/postheader.html") rescue ""
|
186
|
+
@posttail ||= File.read("#{vdir}/posttrailer.html") rescue ""
|
187
|
+
File.open("#{dir}/index.html", "w") do |f|
|
188
|
+
f.puts @posthead
|
189
|
+
f.puts @meta.body
|
190
|
+
f.puts @posttail
|
191
|
+
end
|
192
|
+
generate_index(view)
|
193
|
+
end
|
194
|
+
|
195
|
+
### link_post
|
196
|
+
|
197
|
+
def link_post(meta)
|
198
|
+
# First gather the views
|
199
|
+
views = meta.views
|
200
|
+
views.each {|view| puts "Handling view '#{view}'"; link_post_view(view) }
|
201
|
+
end
|
202
|
+
|
203
|
+
### rebuild
|
204
|
+
|
205
|
+
def rebuild
|
206
|
+
files = Dir.entries("#{@config.root}/src/").grep /\d\d\d\d.*.lt3$/
|
207
|
+
files = files.sort.reverse
|
208
|
+
files.each do |file|
|
209
|
+
reload_post(file)
|
210
|
+
link_post(@meta)
|
211
|
+
publish_post
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|
215
|
+
### relink
|
216
|
+
|
217
|
+
def relink
|
218
|
+
@config.views.each do |view|
|
219
|
+
generate_index(view)
|
220
|
+
end
|
221
|
+
end
|
222
|
+
|
223
|
+
### publish?
|
224
|
+
|
225
|
+
def publish?
|
226
|
+
yn = ask("Publish? y/n ")
|
227
|
+
yn.upcase == "Y"
|
228
|
+
end
|
229
|
+
|
230
|
+
### publish_post
|
231
|
+
|
232
|
+
def publish_post
|
233
|
+
# Grab destination data
|
234
|
+
# scp changed files over
|
235
|
+
puts " Publish: Not implemented yet"
|
236
|
+
end
|
237
|
+
|
238
|
+
### list_views
|
239
|
+
|
240
|
+
def list_views
|
241
|
+
read_config unless @config
|
242
|
+
puts @config.views
|
243
|
+
end
|
244
|
+
|
245
|
+
### new_view
|
246
|
+
|
247
|
+
def new_view(arg = nil)
|
248
|
+
arg = nil if arg == ""
|
249
|
+
read_config unless @config
|
250
|
+
arg ||= ask("New view: ") # check validity later
|
251
|
+
raise "view #{arg} already exists" if @config.views.include?(arg)
|
252
|
+
dir = @config.root + "/views/" + arg
|
253
|
+
cmd = "mkdir -p #{dir}/custom"
|
254
|
+
system(cmd)
|
255
|
+
@config.views << arg
|
256
|
+
end
|
257
|
+
|
258
|
+
### import
|
259
|
+
|
260
|
+
def import(arg = nil)
|
261
|
+
read_config unless @config
|
262
|
+
arg = nil if arg == ""
|
263
|
+
arg ||= ask("Filename: ") # check validity later
|
264
|
+
name = arg
|
265
|
+
grep = `grep ^.title #{name}`
|
266
|
+
@title = grep.sub(/^.title /, "")
|
267
|
+
@slug = make_slug(@title)
|
268
|
+
@fname = @slug + ".lt3"
|
269
|
+
system("cp #{name} #{@config.root}/src/#@fname")
|
270
|
+
edit_post(@fname)
|
271
|
+
process_post(@fname)
|
272
|
+
if publish?
|
273
|
+
link_post(@meta)
|
274
|
+
publish_post
|
275
|
+
end
|
276
|
+
end
|
277
|
+
|
278
|
+
### new_post
|
279
|
+
|
280
|
+
def new_post
|
281
|
+
read_config unless @config
|
282
|
+
@title = ask("Title: ")
|
283
|
+
@today = Time.now.strftime("%Y%m%d")
|
284
|
+
@date = Time.now.strftime("%Y-%m-%d")
|
285
|
+
|
286
|
+
file = create_empty_post
|
287
|
+
edit_post(file)
|
288
|
+
process_post(file)
|
289
|
+
if publish?
|
290
|
+
link_post(@meta)
|
291
|
+
publish_post
|
292
|
+
end
|
293
|
+
end
|
294
|
+
|
data/runeblog.gemspec
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'date'
|
2
|
+
require 'find'
|
3
|
+
|
4
|
+
$: << "."
|
5
|
+
require "lib/runeblog"
|
6
|
+
|
7
|
+
Gem::Specification.new do |s|
|
8
|
+
system("rm -f *.gem")
|
9
|
+
s.name = 'runeblog'
|
10
|
+
s.version = RuneBlog::VERSION
|
11
|
+
s.date = Date.today.strftime("%Y-%m-%d")
|
12
|
+
s.summary = "A command-line blogging system"
|
13
|
+
s.description = "A blog system based on Ruby and Livetext"
|
14
|
+
s.authors = ["Hal Fulton"]
|
15
|
+
s.email = 'rubyhacker@gmail.com'
|
16
|
+
s.executables << "blog"
|
17
|
+
|
18
|
+
# Files...
|
19
|
+
main = Find.find("bin").to_a + Find.find("lib").to_a
|
20
|
+
misc = %w[./README.lt3 ./README.md runeblog.gemspec]
|
21
|
+
test = Find.find("test").to_a
|
22
|
+
|
23
|
+
s.files = main + misc + test
|
24
|
+
s.homepage = 'https://github.com/Hal9000/runeblog'
|
25
|
+
s.license = "Ruby's license"
|
26
|
+
end
|
metadata
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: runeblog
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Hal Fulton
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2017-04-05 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description: A blog system based on Ruby and Livetext
|
14
|
+
email: rubyhacker@gmail.com
|
15
|
+
executables:
|
16
|
+
- blog
|
17
|
+
extensions: []
|
18
|
+
extra_rdoc_files: []
|
19
|
+
files:
|
20
|
+
- "./README.lt3"
|
21
|
+
- "./README.md"
|
22
|
+
- bin/blog
|
23
|
+
- lib/runeblog.rb
|
24
|
+
- runeblog.gemspec
|
25
|
+
homepage: https://github.com/Hal9000/runeblog
|
26
|
+
licenses:
|
27
|
+
- Ruby's license
|
28
|
+
metadata: {}
|
29
|
+
post_install_message:
|
30
|
+
rdoc_options: []
|
31
|
+
require_paths:
|
32
|
+
- lib
|
33
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
34
|
+
requirements:
|
35
|
+
- - ">="
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0'
|
38
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
39
|
+
requirements:
|
40
|
+
- - ">="
|
41
|
+
- !ruby/object:Gem::Version
|
42
|
+
version: '0'
|
43
|
+
requirements: []
|
44
|
+
rubyforge_project:
|
45
|
+
rubygems_version: 2.2.2
|
46
|
+
signing_key:
|
47
|
+
specification_version: 4
|
48
|
+
summary: A command-line blogging system
|
49
|
+
test_files: []
|