runeblog 0.0.1

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