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 quit
puts
exit
end
### version
def version
puts "\n " + RuneBlog::VERSION
end
### new_blog!
def new_blog!
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
### open_remote
def open_remote
@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 deploy
# TBD clunky FIXME
@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 rebuild
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 relink
@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 list_views
abort "Config file not read" unless @config
puts
@config.views.each {|v| puts " #{v}" }
rescue => err
error(err, __LINE__, __FILE__)
end
### change_view
def change_view(arg = nil)
if arg.nil?
puts "\n #@view"
else
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 new_view(arg = nil)
arg = nil if arg == ""
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')
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 new_post
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 remove_post(arg, safe=true)
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 edit_post(arg)
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 list_posts
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 list_drafts
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
end