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.
Files changed (7) hide show
  1. checksums.yaml +7 -0
  2. data/README.lt3 +35 -0
  3. data/README.md +35 -0
  4. data/bin/blog +58 -0
  5. data/lib/runeblog.rb +294 -0
  6. data/runeblog.gemspec +26 -0
  7. 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}&nbsp;&nbsp;</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: []