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