Web から楽にサイトを更新したいというよくあるアレ。
ただの Wiki 形式だと出力できるのが固定に layout された html だけなので、いろいろやっています。
まず何も考えずにページを新規作成すると Markdown として扱われます。
ページの行頭に title ほげふが と入れておくとページタイトルがほげふがになります。
頭に haml *title と入れておくと、 haml としてレンダリングされます。自由なレイアウトのページも Web 上から更新することが出来ます。 haml 内で Ruby のコードを記述することも出来、とても幸せになれます。
頭に builder *title と入れておくと、 builder テンプレートとしてレンダリングされます。 RSS など XML を吐きたい時に便利。 Ruby を書けるので幸せになれます。
頭に raw でいれたものをそのまま吐きます。 js をアップロードしたり貼ったりしたい時に便利。
頭に ruby で ruby のコードとして評価した結果を返す。何かに使えて便利。
以下のソースを読めば分かると思いますが、 /source/app.rb にソースがある場合はそれを使います。ソースすらサイトから更新出来るようにした。
require 'bundler'
Bundler.require
REDIS = Redis::Namespace.new :ssig33com, url: (ENV['REDIS_URL'] || 'redis://localhost:6379/')
PASSWORD = open('password').read.chomp
ActiveRecord::Base.establish_connection JSON.parse(open('dbconfig.json').read)
ActiveRecord::Base.logger = Logger.new(STDOUT)
AWS_ID=ENV['AWS_ID']
AWS_KEY=ENV['AWS_KEY']
class Page < ActiveRecord::Base
end
configure do
set :allow_origin, :any
set :allow_methods, [:get, :post]
set :allow_credentials, true
set :server, :puma
end
begin
code = Page.where(name: "source/app.rb").order("created_at desc").first
ary = code.body.split("\n")
ary.shift
eval ary.join("\n")
rescue StandardError, LoadError,SyntaxError => e
p e
class Page < ActiveRecord::Base
def html request
if self.body.split("\n").first =~ /^haml/
return self.haml request
elsif self.body.split("\n").first =~ /^ruby/
return "#{self.body}
"
elsif self.body.split("\n").first =~ /^builder/ or self.body.split("\n").first =~ /^raw/
return "#{self.body}
"
elsif self.body.split("\n").first =~ /^title/
str = ""
ary = self.body.split("\n")
str = ""
ary.shift
str = ary.join("\n")
else
str = self.body
end
html = BlueCloth.new(str, :fenced_code, :gh_blockcode).to_html rescue "#{self.body}
"
html
end
def title
if self.body.split("\n").first =~ /title/
str = self.body.split("\n").shift.split("title\ ").last
elsif self.body.split("\n").first =~ /haml/
str = self.body.split("\n").shift.split("haml\ ").last
elsif self.body.split("\n").first =~ /builder/
str = self.body.split("\n").shift.split("builder\ ").last
elsif self.body.split("\n").first =~ /ruby/
str = self.body.split("\n").shift.split("ruby\ ").last
else
str = self.name
end
str.chomp!
str = self.name if str == nil or str == "" or str == "haml"
str.gsub(/=created_at=/, self.created_at.getutc.to_s)
end
def raw
ary = self.body.split("\n")
ary.shift
ary.join("\n")
end
def haml request
ary = self.body.split("\n")
str = ""
ary.shift
str = ary.join("\n")
Haml::Engine.new(str).render
end
def ruby request
ary = self.body.split("\n")
str = ""
ary.shift
str = ary.join("\n")
eval str
end
def builder request
ary = self.body.split("\n")
str = ""
ary.shift
str = ary.join("\n")
xml = ::Builder::XmlMarkup.new(:indent => 2)
xml.to_s
eval str
end
def layout
ary = self.body.split("\n")
ary.shift
ary.join("\n")
end
end
configure do
set :logging, false
set :app_file, __FILE__
use Rack::Session::Cookie, :secret => 'fsdjkfhsjkhr23f8fhsdjkvhnsdjhrfuiscflaaadn8or'
use Rack::Csrf, :raise => true
end
get '/' do
@page = Page.where(:name => "index").order("created_at desc").first
redirect '/edit/index' unless @page
if layout = Page.where(:name => "layout").order("created_at desc").first
return haml layout.layout
else
return haml :page
end
end
get %r{/edit/(.*)} do
@id = params[:captures].first.to_s
@page = Page.where(:name => @id).order("created_at desc").first
@page = Page.new if @page == nil
haml :edit
end
get %r{/(.*)} do
id = params[:captures].first.to_s
id.chop! if id.reverse[0] == "/"
file = open("public/#{id}/index.html").read rescue nil
return file if file
@page = Page.where(:name => id).order("created_at desc").first
redirect "/edit/#{id}" unless @page
return @page.haml(request) if @page.body.split("\n").first =~ /^haml/
return @page.raw if @page.body.split("\n").first =~ /^raw/ or @page.body.split("\n").first =~ /^layout/
if @page.body.split("\n").first =~ /^builder/
content_type :xml
return @page.builder(request)
end
return @page.ruby(request) if @page.body.split("\n").first =~ /^ruby/
if layout = Page.where(:name => "layout").order("created_at desc").first
return haml layout.layout
else
return haml :page
end
end
post '/update' do
if Digest::MD5.hexdigest(params[:password]).to_s != PASSWORD
return "ERROR"
else
page = Page.new
page.name = params[:id]
page.body = params[:body]
page.save
redirect "/#{params[:id]}"
end
end
post %r{/(.*)} do
id = params[:captures].first.to_s
id.chop! if id.reverse[0] == "/"
file = open("public/#{id}/index.html").read rescue nil
return file if file
@page = Page.where(:name => id).order("created_at desc").first
redirect "/edit/#{id}" unless @page
return @page.haml(request) if @page.body.split("\n").first =~ /^haml/
return @page.raw if @page.body.split("\n").first =~ /^raw/ or @page.body.split("\n").first =~ /^layout/
if @page.body.split("\n").first =~ /^builder/
content_type :xml
return @page.builder(request)
end
return @page.ruby(request) if @page.body.split("\n").first =~ /^ruby/
if layout = Page.where(:name => "layout").order("created_at desc").first
return haml layout.layout
else
return haml :page
end
end
post '/destroy' do
raise if Digest::MD5.hexdigest(params[:password]).to_s != PASSWORD
Page.where(:name => params[:id]).delete_all
redirect "/"
end
error do
"error... #{env['sinatra.error']}"
end
helpers do
def h str
CGI.escapeHTML str.to_s
end
def title
if request.path_info == "/" or request.path_info == "/index"
return "ssig33.com"
else
return "ssig33.com - #{@page.title}"
end
end
end
end
__END__
@@ page
%meta{:charset => "UTF-8"}
%title=h title
%link{:href => "http:https://unpkg.com/sakura.css/css/sakura.css", :media => "screen", :rel => "stylesheet", :type => "text/css"}
%link{:href => "http://ssig33.com/sh_style.css", :media => "screen", :rel => "stylesheet", :type => "text/css"}
%meta{:name => "viewport", :content => "width=320, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"}
%link{:href => '/feed', :rel => 'alternate', :title => 'feed', :type => "application/rss+xml"}
%script{:src => "/sh_main.min.js"}
%script{:src => "/sh_ext.js"}
%script{:src => "/jquery-1.6.1.min.js"}
%div#all~@page.html(request)
:javascript
$.each($("pre code"), function(){
var code = $(this);
var text = code.text();
var pre = $(code.parent());
code.remove();
pre.text(text);
var lang = pre.attr("lang");
if(lang != undefined){
pre.addClass("sh_"+lang);
}
})
sh_highlightDocument();
@@ edit
%meta{:charset => "UTF-8"}
%title=h "Edit - #{params[:captures].first.to_s}"
%link{:href => "http:https://unpkg.com/sakura.css/css/sakura.css", :media => "screen", :rel => "stylesheet", :type => "text/css"}
%meta{:name => "viewport", :content => "width=320, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"}
%div#all
%form{:action => "/update", :method => "post"}
=Rack::Csrf.csrf_tag(env)
%input{:id => "id", :name => "id", :type => "hidden", :value => "#{@id}"}
%p
%textarea{:cols => "80", :id => "bodY", :name => "body", :rows => "30"}=@page.body #rescue ""
%p
%input{:id => "password", :name => "password", :type => "password"}
%p
%input{:name => "commit", :type => "submit", :value => "Save changes"}
%form{:action => "/destroy", :method => "post"}
class CreatePages < ActiveRecord::Migration
def self.up
create_table :pages do |t|
t.string :name
t.text :body, limit: 16777216
t.timestamps
end
add_index :pages, [:name, :created_at]
add_index :pages, :name
end
def self.down
drop_table :pages
end
end