require 'runeblog'
require 'ostruct'
module RuneBlog::REPL
def clear
puts "\e[H\e[2J" # clear screen
end
def red(text)
"\e[31m#{text}\e[0m"
end
def blue(text)
"\e[34m#{text}\e[0m"
end
def bold(str)
"\e[1m#{str}\e[22m"
end
def interpolate(str)
wrap = "<<-EOS\n#{str}\nEOS"
eval wrap
end
def colored_slug(slug)
red(slug[0..3])+blue(slug[4..-1])
end
### error
def error(err, line, file)
str = "\n Error: (line #{line} of #{File.basename(file)}) "
str << err.to_s
puts red(str)
puts err.backtrace
end
### ask
def ask(prompt, meth = :to_s)
print prompt
STDOUT.flush
STDIN.gets.chomp.send(meth)
end
### quit
def cmd_quit(arg)
raise "Glitch: Got an argument" if arg != []
puts
exit
end
### version
def cmd_version(arg)
raise "Glitch: Got an argument" if arg != []
puts "\n " + RuneBlog::VERSION
end
### new_blog!
def cmd_new_blog!(arg)
raise "Glitch: Got an argument" if arg != []
unless File.exist?(".blog")
yn = ask(red(" No .blog found. Create new blog? "))
if yn.upcase == "Y"
#-- what if data already exists?
result = system("cp -r #{RuneBlog::DefaultData} .")
raise "Error copying default data" unless result
File.open(".blog", "w") do |f|
f.puts "data"
f.puts "no_default"
end
File.open("data/VERSION", "a") {|f| f.puts "\nBlog created: " + Time.now.to_s }
end
end
rescue => err
error(err, __LINE__, __FILE__)
end
### make_slug
def make_slug(title, seq=nil)
num = '%04d' % (seq || @config.next_sequence) # FIXME can do better
slug = title.downcase.strip.gsub(' ', '-').gsub(/[^\w-]/, '')
"#{num}-#{slug}"
end
### read_config
def read_config
# Crude - FIXME later
cfg_file = ".blog"
new_blog! unless File.exist?(cfg_file)
@config = RuneBlog::Config.new(cfg_file)
@view = @config.view # current view
@sequence = @config.sequence
@root = @config.root
@config
rescue => err
error(err, __LINE__, __FILE__)
end
### create_empty_post
def create_empty_post
@template = <<-EOS
.mixin liveblog
.title #@title
.pubdate #@date
.views #@view
.teaser
Teaser goes here.
.end
Remainder of post goes here.
EOS
@slug = make_slug(@title)
@fname = @slug + ".lt3"
File.open("#@root/src/#@fname", "w") {|f| f.puts @template }
@fname
rescue => err
error(err, __LINE__, __FILE__)
end
### edit_initial_post
def edit_initial_post(file)
result = system("vi #@root/src/#{file} +8 ")
raise "Problem editing #@root/src/#{file}" unless result
rescue => err
error(err, __LINE__, __FILE__)
end
### browse
def cmd_browse
raise "Glitch: Got an argument" if arg != []
@deploy ||= {}
return puts red("\n Deploy first.") unless @deploy[@view]
lines = @deploy[@view]
user, server, sroot, spath = *lines
result = system("open 'http://#{server}/#{spath}'")
raise "Problem opening http://#{server}/#{spath}" unless result
rescue => err
error(err, __LINE__, __FILE__)
end
### open_local
def open_local
result = system("open #{@config.viewdir(@view)}/index.html")
raise "Problem opening #{@config.viewdir(@view)}/index.html" unless result
rescue => err
error(err, __LINE__, __FILE__)
end
def cmd_deploy(arg)
# TBD clunky FIXME
raise "Glitch: Got an argument" if arg != []
@deploy ||= {}
deployment = @config.viewdir(@view) + "deploy"
raise "File '#{deployment}' not found" unless File.exist?(deployment)
lines = File.readlines(deployment).map {|x| x.chomp }
@deploy[@view] = lines
user, server, sroot, spath = *lines
vdir = @config.viewdir(@view)
files = ["#{vdir}/index.html"]
files += Dir.entries(vdir).grep(/^\d\d\d\d/).map {|x| "#{vdir}/#{x}" }
files.reject! {|f| File.mtime(f) < File.mtime("#{vdir}/last_deployed") }
return puts red("\n No files to deploy") if files.empty?
puts "\n Files:"
files.each {|f| puts " " + f }
puts
dir = "#{sroot}/#{spath}"
# FIXME - may or may not already exist
result = system("ssh root@#{server} mkdir #{dir}")
cmd = "scp -r #{files.join(' ')} root@#{server}:#{dir} >/dev/null 2>&1"
print red("\n Deploying #{files.size} files... ")
result = system(cmd)
raise "Problem occurred in deployment" unless result
File.write("#{vdir}/last_deployed", files)
puts red("finished.")
rescue => err
error(err, __LINE__, __FILE__)
end
### process_post
def process_post(file)
@main ||= Livetext.new
@main.main.output = File.new("/tmp/WHOA","w")
path = @root + "/src/#{file}"
@meta = @main.process_file(path, binding)
raise "process_file returned nil" if @meta.nil?
@meta.slug = make_slug(@meta.title, @config.sequence)
@meta.slug = file.sub(/.lt3$/, "")
@meta
rescue => err
error(err, __LINE__, __FILE__)
end
### reload_post
def reload_post(file)
@main ||= Livetext.new
@main.main.output = File.new("/tmp/WHOA","w")
@meta = process_post(file)
@meta.slug = file.sub(/.lt3$/, "")
@meta
rescue => err
error(err, __LINE__, __FILE__)
end
### posting
def posting(view, meta)
ref = "#{view}/#{meta.slug}/index.html"
<<-HTML
#{meta.pubdate}
#{meta.title}
#{meta.teaser}
Read more...
HTML
end
### generate_index
def generate_index(view)
# Gather all posts, create list
vdir = "#@root/views/#{view}"
posts = Dir.entries(vdir).grep /^\d\d\d\d/
posts = posts.sort.reverse
# Add view header/trailer
head = File.read("#{vdir}/custom/blog_header.html") rescue RuneBlog::BlogHeader
tail = File.read("#{vdir}/custom/blog_trailer.html") rescue RuneBlog::BlogTrailer
@bloghead = interpolate(head)
@blogtail = interpolate(tail)
# Output view
posts.map! {|post| YAML.load(File.read("#{vdir}/#{post}/metadata.yaml")) }
File.open("#{vdir}/index.html", "w") do |f|
f.puts @bloghead
posts.each {|post| f.puts posting(view, post) }
f.puts @blogtail
end
rescue => err
error(err, __LINE__, __FILE__)
end
### create_dir
def create_dir(dir)
cmd = "mkdir -p #{dir} >/dev/null 2>&1"
result = system(cmd)
raise "Can't create #{dir}" unless result
end
### link_post_view
def link_post_view(view)
# Create dir using slug (index.html, metadata?)
vdir = @config.viewdir(view)
dir = vdir + @meta.slug + "/"
create_dir(dir + "assets")
File.write("#{dir}/metadata.yaml", @meta.to_yaml)
template = File.read(vdir + "custom/post_template.html")
post = interpolate(template)
File.write(dir + "index.html", post)
generate_index(view)
rescue => err
error(err, __LINE__, __FILE__)
end
### find_asset
# def find_asset(asset) # , views)
# # STDERR.puts "repl find_asset: @meta = #{@meta.inspect}"
# views = @meta.views
# views.each do |view|
# vdir = @config.viewdir(view)
# post_dir = "#{vdir}#{@meta.slug}/assets/"
# path = post_dir + asset
# STDERR.puts " Seeking #{path}"
# return path if File.exist?(path)
# end
# views.each do |view|
# dir = @config.viewdir(view) + "/assets/"
# path = dir + asset
# STDERR.puts " Seeking #{path}"
# return path if File.exist?(path)
# end
# top = @root + "/assets/"
# path = top + asset
# STDERR.puts " Seeking #{path}"
# return path if File.exist?(path)
#
# return nil
# end
#
# ### find_all_assets
#
# def find_all_assets(list, views)
# # STDERR.puts "\n Called find_all_assets with #{list.inspect}"
# list ||= []
# list.each {|asset| puts "#{asset} => #{find_asset(asset, views)}" }
# end
### publish_post
def publish_post(meta)
puts " #{colored_slug(meta.slug)}"
# First gather the views
views = meta.views
print " Views: "
views.each do |view|
print "#{view} "
link_post_view(view)
end
# assets = find_all_assets(@meta.assets, views)
puts
rescue => err
error(err, __LINE__, __FILE__)
end
### rebuild_post
def rebuild_post(file)
reload_post(file)
publish_post(@meta) # FIXME ??
rescue => err
error(err, __LINE__, __FILE__)
end
### rebuild
def cmd_rebuild(arg)
raise "Glitch: Got an argument" if arg != []
puts
files = Dir.entries("#@root/src/").grep /\d\d\d\d.*.lt3$/
files.map! {|f| File.basename(f) }
files = files.sort.reverse
files.each {|file| rebuild_post(file) }
rescue => err
error(err, __LINE__, __FILE__)
end
### relink
def cmd_relink(arg)
raise "Glitch: Got an argument" if arg != []
@config.views.each {|view| generate_index(view) }
rescue => err
error(err, __LINE__, __FILE__)
end
# ### publish?
#
# def publish?
# yn = ask(red(" Publish? y/n "))
# yn.upcase == "Y"
# end
### list_views
def cmd_list_views(arg)
abort "Config file not read" unless @config
raise "Glitch: Got an argument" if arg != []
puts
@config.views.each {|v| puts " #{v}" }
rescue => err
error(err, __LINE__, __FILE__)
end
### change_view
def cmd_change_view(arg)
if arg.empty?
puts "\n #@view"
else
arg = arg.first
list = @config.views.grep /^#{arg}/
if list.size == 1
@view = @config.view = list.first
puts red("\n View: #{@view}") if arg != @view
else
puts "view #{arg.inspect} does not exist"
end
end
rescue => err
error(err, __LINE__, __FILE__)
end
### new_view
def cmd_new_view(arg)
arg = arg.first
read_config unless @config
arg ||= ask("New view: ") # check validity later
raise "view #{arg} already exists" if @config.views.include?(arg)
dir = @root + "/views/" + arg + "/"
create_dir(dir + 'custom')
create_dir(dir + 'assets')
# Something more like this? RuneBlog.new_view(arg)
File.write(dir + "custom/blog_header.html", RuneBlog::BlogHeader)
File.write(dir + "custom/blog_trailer.html", RuneBlog::BlogTrailer)
File.write(dir + "last_deployed", "Initial creation")
@config.views << arg
rescue => err
error(err, __LINE__, __FILE__)
end
### import
def import(arg = nil)
read_config unless @config
arg = nil if arg == ""
arg ||= ask("Filename: ") # check validity later
name = arg
grep = `grep ^.title #{name}`
@title = grep.sub(/^.title /, "")
@slug = make_slug(@title)
@fname = @slug + ".lt3"
result = system("cp #{name} #@root/src/#@fname")
raise "Could not copy #{name} to #@root/src/#@fname" unless result
edit_initial_post(@fname)
process_post(@fname)
publish_post(@meta) # if publish?
rescue => err
error(err, __LINE__, __FILE__)
end
### new_post
def cmd_new_post(arg)
raise "Glitch: Got an argument" if arg != []
read_config unless @config
@title = ask("Title: ")
@today = Time.now.strftime("%Y%m%d")
@date = Time.now.strftime("%Y-%m-%d")
file = create_empty_post
edit_initial_post(file)
# file = @root + "/src/" + file
process_post(file) #- FIXME handle each view
publish_post(@meta) # if publish?
rescue => err
error(err, __LINE__, __FILE__)
end
### remove_multiple_posts
def remove_multiple_posts(str)
args = str.split
args.each {|arg| remove_post(arg, false) }
end
### remove_post
#-- FIXME affects linking, building, deployment...
def cmd_remove_post(arg, safe=true)
arg = arg.first
id = Integer(arg) rescue raise("'#{arg}' is not an integer")
tag = "#{'%04d' % id}-"
files = Find.find(@root).to_a
files = files.grep(/#{tag}/)
return puts red("\n No such post found (#{tag})") if files.empty?
if safe
puts
files.each {|f| puts " #{f}" }
ques = files.size > 1 ? "\n Delete all these? " : "\n Delete? "
yn = ask red(ques)
if yn.downcase == "y"
result = system("rm -rf #{files.join(' ')}")
raise "Problem deleting file(s)" unless result
puts red("\n Deleted")
else
puts red("\n No action taken")
end
else
result = system("rm -rf #{files.join(' ')}")
puts red("\n Deleted:")
files.each {|f| puts " #{f}" }
raise "Problem mass-deleting file(s)" unless result
end
rescue => err
puts err
puts err.backtrace
puts
end
### edit_post
#-- FIXME affects linking, building, deployment...
def cmd_edit_post(arg)
arg = arg.first
id = Integer(arg) rescue raise("'#{arg}' is not an integer")
tag = "#{'%04d' % id}-"
files = Find.find(@root+"/src").to_a
files = files.grep(/#{tag}/)
files = files.map {|f| File.basename(f) }
return puts red("Multiple files: #{files}") if files.size > 1
return puts red("\n No such post found (#{tag})") if files.empty?
file = files.first
result = system("vi #@root/src/#{file}")
raise "Problem editing #{file}" unless result
rebuild_post(file)
rescue => err
puts err
puts err.backtrace
puts
end
### list_posts
def cmd_list_posts(arg)
raise "Glitch: Got an argument" if arg != []
dir = @config.viewdir(@view)
Dir.chdir(dir) do
posts = Dir.entries(".").grep(/^0.*/)
if posts.empty?
puts "\n " + @view + ":" + red(" No posts")
else
puts "\n " + @view + ":\n "
posts.each {|post| puts " #{colored_slug(post)}" }
end
end
rescue
puts "Oops? cwd = #{Dir.pwd} dir = #{dir}"
puts err.backtrace
exit
end
### list_drafts
def cmd_list_drafts(arg)
raise "Glitch: Got an argument" if arg != []
dir = "#@root/src"
Dir.chdir(dir) do
posts = Dir.entries(".").grep(/^0.*.lt3/)
puts
if posts.empty?
puts red(" No posts")
else
posts.each {|post| puts " #{colored_slug(post.sub(/.lt3$/, ""))}" }
end
end
rescue
puts "Oops? cwd = #{Dir.pwd} dir = #{dir}"
puts err.backtrace
exit
end
def cmd_INVALID(arg)
puts "\n Command '#{red(arg)}' not understood.\n "
end
def cmd_help(arg)
raise "Glitch: Got an argument" if arg != []
puts <<-EOS
Commands:
h, help This message
q, quit Exit the program
change view VIEW Change current view
cv VIEW Change current view
new view Create a new view
list views List all views available
lsv Same as: list views
p, post Create a new post
new post Same as post (create a post)
lsp, list posts List posts in current view
lsd, list drafts List all posts regardless of view
rm ID Remove a post
edit ID Edit a post
preview Look at current (local) view in browser
browse Look at current (deployed) view in browser
relink Regenerate index for all views
rebuild Regenerate all posts and relink
deploy Deploy (current view)
EOS
end
end