Total Pageviews

Saturday, 28 June 2014

在linux vps上搭建基于lein的静态博客程序:madness(这个不错)

git clone https://github.com/algernon/madness madness-site
cd madness-site

root@as3:~/madness-site# ls
project.clj  README.md  resources  settings.clj  src 
root@as3:~/madness-site# lein madness (这个就是生成/更新静态网站的命令)
root@as3:~/madness-site# ls
project.clj  public  README.md  resources  settings.clj  src  target
(生成了public目录)
root@as3:~/madness-site# cd public
root@as3:~/madness-site/public# ls
blog  index.html
(可见~/madness-site/public/就是静态网站的根目录)
root@as3:~/madness-site/public# nohup python -m SimpleHTTPServer 24385 > /dev/null &
访问http://as3.brite.biz:24385/即可看到网站效果。

发贴方法:
root@as3:~/madness-site/public# cd ../resources/posts
root@as3:~/madness-site/resources/posts# nano 2014-06-28-test1.markdown
格式为:
---
title: 测试1
date: 2014-06-28 19:43
tags: [misc1, misc2]
---

这是测试1.

root@as3:~/madness-site/resources/posts# cd ../..
root@as3:~/madness-site# lein madness

演示站点:http://as3.brite.biz:24385/,http://as3.brite.biz:24385/blog/archives/,
http://mn.brite.biz.st,http://mn.brite.biz.st/blog/archives/
程序作者用此程序搭建的博客:http://asylum.madhouse-project.org/
项目地址:https://github.com/algernon/madness

类似的程序http://briteming.blogspot.co.uk/2014/06/linux-vpsstasisblog-genaugustl.html,不过这个madness比blog-gen和augustl要好,madness的帖子是按时间顺序排列的。

在linux vps上搭建基于stasis的静态博客程序:blog-gen和augustl

git clone https://github.com/gilbertw1/blog-gen blog-gen-site
cd blog-gen-site

root@as3:~/blog-gen-site# ls
project.clj  README.md  resources  src  test
root@as3:~/blog-gen-site# lein build-site (这个就是生成/更新静态网站的根目录的命令)
root@as3:~/blog-gen-site# ls
dist  project.clj  README.md  resources  src  target  test
(新出现了dist目录和target目录)
root@as3:~/blog-gen-site# cd dist
root@as3:~/blog-gen-site/dist# ls
01c3be5bd3b1    archive                 dc22c5a50c24         misc2-atom.xml
0f215576fa3c    atom.xml                f780eae56b8c         misc3-atom.xml
2f303a09b7af    blog                    fd64f70a4242         play2-atom.xml
3cfdb5bc6ad2    c731172af9aa            images               resume.html
54424dece1ba    clojure-atom.xml        index.html           rxjava-atom.xml
684c38ecc30f    clojurescript-atom.xml  iteratees-atom.xml   scala-atom.xml
72777fb205ae    code                    javascript-atom.xml  tags
7f4aef274857    core.async-atom.xml     macros-atom.xml      tutorial-atom.xml
anorm-atom.xml  css                     misc1-atom.xml
(里面有index.html,可见~/blog-gen-site/dist/就是静态网站的根目录)
root@as3:~/blog-gen-site/dist# nohup Rwebserver 32153 > /dev/null &
访问http://as3.brite.biz:32153/即可看到网站效果。

发贴方法:
root@as3:~/blog-gen-site/dist# cd ../resources/posts
root@as3:~/blog-gen-site/resources/posts# ls
2013-07-01-anorm-pk-json.md
2013-07-05-action-composition-auth.md
2013-07-19-escaping-callback-hell-with-core-async.md
2013-07-30-anatomy-of-a-clojure-macro.md
2013-10-22-rxPlay-making-iteratees-and-observables-play-nice.md
2013-11-03-rx-the-importance-of-honoring-unsubscribe.md
root@as3:~/blog-gen-site/resources/posts# nano 2014-06-28-this-is-test1.md
按里面某个md文件的格式,新建帖子2014-06-28-this-is-test1.md,格式为:
---
title : 测试1
tags : [misc1, misc2, misc3]
---

这是测试1.


然后,
root@as3:~/blog-gen-site/resources/posts# cd ../..
root@as3:~/blog-gen-site# lein build-site

演示站点:http://as3.brite.biz:32153/,http://blog-gen.brite.biz.st/
项目地址:https://github.com/gilbertw1/blog-gen
https://github.com/magnars/stasis

 注意:新建帖子时,文件名最好命名为2014-07-07-1810-jingle-bell.md,
 2014-07-07-1828-black-dot-on-white-paper.md这样的形式,这样所发表的帖子就是按时间顺序排列的了
------------------
类似的源码

git clone https://github.com/augustl/augustl.com augustl.com-site
cd augustl.com-site

root@as3:~/augustl.com-site# ls
deploy.sh   posts  project.clj  README  resources  src 
root@as3:~/augustl.com-site# lein export (这个就是生成/更新静态网站的根目录的命令)
root@as3:~/augustl.com-site# ls
deploy.sh  dist  posts  project.clj  README  resources  src  target
(生成了dist目录)
root@as3:~/augustl.com-site# cd dist
root@as3:~/augustl.com-site/dist# ls
02d2ad4edc12  9516652d3f2b  about     blog          fe4800707436  stylesheets
275476795ca3  9a19b1b112de  archive   dump          index.html
28c59dd5d58c  a051501decb0  atom.xml  fdd30613eedd  static
(可见~/augustl.com-site/dist/就是静态网站的根目录)
root@as3:~/augustl.com-site/dist# nohup Rwebserver 32154 > /dev/null &
访问http://as3.brite.biz:32154/即可看到网站效果。

发贴方法:
root@as3:~/augustl.com-site/dist# cd ../posts/blog/2014/
root@as3:~/augustl.com-site/posts/blog/2014# nano super_talk.html
格式为:
date: 2014.06.28
title: 高论

不花钱与女人发生关系,是一夜情。。。


然后,
root@as3:~/augustl.com-site/posts/blog/2014# cd ~/augustl.com-site
root@as3:~/augustl.com-site# lein export

演示站点:http://as3.brite.biz:32154/,http://augustl.brite.biz.st/
项目地址:https://github.com/augustl/augustl.com
https://github.com/magnars/stasis

相关帖子:http://briteming.blogspot.co.uk/2014/06/building-static-sites-in-clojure-with.html

stasis是基于java和lein的博客程序。lein的安装见这里:
http://briteming.blogspot.co.uk/2014/03/linux-vpsjavaclojure-nakkaya-static.html

在linux vps上搭建基于python的博客程序-pomash

git clone https://github.com/JmPotato/Pomash pomash-site
cd pomash-site

root@as3:~/pomash-site# ls
init_db.py  LICENSE  Pomash  README.md  requirements.txt  run.py  settings.py
root@as3:~/pomash-site# pip install -r requirements.txt
root@as3:~/pomash-site# nano settings.py
root@as3:~/pomash-site# python init_db.py
Did not find any database file.
Creat Admin Config......
Creat Article Database......
Creat Page Database......
Creat Tag Database......
Creat Index......
Successful to creat database file.
root@as3:~/pomash-site# nohup python run.py --port=3526 > /dev/null &
访问http://as3.brite.biz:3526/即可看到网站效果。
搭配的数据库好像是sqlite.

演示站点:http://as3.brite.biz:3526/
(http://as3.brite.biz:3527/,这个是另外克隆源码到 ~/pomash-site2,在~/pomash-site2/上搭建的。)
项目地址:https://github.com/JmPotato/Pomash

Friday, 27 June 2014

从英德海战看海权

世界范围内的斗争是获得者与未获得者之间的斗争——alfred  sayer mahan
  “第一次世界大战的缘由之一”
  1893年,当阿尔弗雷德o塞耶o马汉上校指挥的“芝加哥”号巡洋舰抵达英国南安普敦港时,这位终于被海军高层从美国海军战争学院“驱赶”到军舰上任职的上校,多少有些受宠若惊地发现,他在这个世界头号海军强国已经被赞颂为了“新哥白尼”。
  在那次访问和翌年的再次访问中,马汉受到了维多利亚女王、她正值来访的外孙德皇威廉二世、后来成为爱德华七世的威尔士亲王、索尔兹伯里侯爵、大 财阀罗思柴尔德男爵以及皇家海军协会的盛情款待。牛津大学和剑桥大学在一周内分别授予了马汉荣誉学位。马汉成为了第一个被破例邀请赴皇家海军俱乐部,出席 纪念豪上将“光荣的六月一日”战役胜利周年纪念宴会的外国军官。曾经一手促成英国国会通过海军两强法案的索尔兹伯里侯爵,迫不及待地对这位美国海军上校表 白:“我们增加支持海军的投票,是因为您的‘海权论’深入人心”。
  21年后,德意志第二帝国皇帝威廉二世,与他那帮子有着扯不清理还乱的血缘关系的欧洲皇室亲戚们之间,爆发了第一次世界大战。按照后来广为流传 的看法,促成这场战争的一个重要原因正是那次21年前的会见:因为威廉二世和他的英国亲戚一样,对马汉所创立的海权论痴迷不已,结果导致其在极度膨胀的虚 荣心驱使下建造了一支外强中干的庞大舰队、自不量力地去挑衅大英帝国主导下的“维多利亚式的和平”,并且最终挑起了一场引发所有欧洲列强兵戎相见的世界大 战。在那场血流成河的大火拼中,欧亚大陆的四大帝国——德意志第二帝国、奥匈帝国、俄罗斯帝国、奥斯曼土耳其帝国土崩瓦解,两千万军人和平民丧生或伤残。
  鉴于巴尔干问题、殖民地问题、英德海军竞赛,常被认为是导致第一次世界大战爆发的三大主因。因此马汉在去世后获得了一顶尴尬的桂冠:“第一次世界大战的缘由之一”。
  毋庸置疑,不管从哪个方面来看,1914~1918年间的第一次世界大战都是野心勃勃的帝国主义国家之间为了争夺赃物而进行的最血腥和最丑陋的 暴行。在这场工业革命以来最残酷的人间悲剧中,来自数十个最先进的“文明国家”的数千万人驾驭着最现代化的战争机器彼此厮杀。在他们的身后是数亿从癫狂到 疲惫的人们在支撑着这场屠杀的持续。仅凭这一点,战争的元凶之一威廉二世,就将被永远钉在历史的耻辱柱上。
  然而,问题在于“海权”在这场战争中究竟扮演着一个怎样的角色?难道德国皇帝对海权的贪婪和由此引发的“海军竞赛”,真的是导致战争的万恶之源吗?
  发展权之争
  德国的问题必须首先从英国,或者说从大不列颠开创的帝国主义时代说起。
  19世纪末,西方列强已经进入了帝国主义时代。与自由资本主义时代不同,此时的剥削已经不再仅仅是一个民族内部的剥削,而且正日益成为了一个先 发民族对其他落后民族的剥削,成为了一个帝国对广大殖民地人民的剥削。反对这种剥削的力量,既来自于被称为“民族主义觉醒”的被压迫民族的反抗,也来自于 新、老帝国主义国家之间的较量,特别是后发帝国与先发帝国之间的斗争。在被自由贸易掩盖下的垄断利益的侧影里,海权的较量不过是先发国家与后发国家之间 “发展权”之争的缩影而已。
  如马汉所言:“世界范围内的斗争是获得者与未获得者之间的斗争。”垄断者们追求利益最大化的本性,使得通过以殖民地、势力范围为标志的排他性贸 易——如果还能被称为贸易的话,获得的国际剪刀差,成为了支撑帝国秩序和经济社会各个方面运转的基本动力。垄断利益的既得者与未得者们的博弈甚至厮杀,成 为了那个时代国际关系与国际秩序的主旋律。在大英帝国看来,正是强势外交带来了排他性战略贸易的超额利润。而对其他国家压倒性的海权垄断则是帝国强势外交 的基础。因此,吓阻他国破坏这种海权垄断的任何努力,自然也就是帝国长治久安的关键。
  回望历史,海权这种上层建筑作为国家暴力机器,无疑是建构于经济基础之上的。正是19世纪末之前的英国在综合国力,特别是经济实力上的遥遥领 先,支撑了皇家海军的绝对海权,并获得了海权回报的垄断利润。什么海权的挑战?问题的核心不过是一个如何取得并保持超额利润的问题。因此,对于海权的竞 争,最直接的基础并不在于舰队,而是来自工厂和工人,也就是经济规模和劳动生产率的竞争!
  海权的基础在于经济实力。一旦这个经济实力对比改变了,那么勉力支撑一个无法继续承担的目标,往往会付出自身毁灭的代价。19世纪末,英国在经 济上对其他列强,尤其是以美国、德国为代表的新兴国家的优势正在日渐缩小,甚至逐渐消失。因此伴随着经济基础的改变,不管有没有来自新兴国家的挑战,经济 规模已经不再是世界第一的大英帝国,对于垄断型海权的控制已日益力不从心。然而在那个时代,英国国内却鲜有人认识到这一点。从首相到平民、从海军元帅到知 识分子,大英帝国上下弥漫的那种永恒统治海洋的幻想和莫名其妙的光荣感,正是那个国家在近一个世纪的时间里对目标与手段的战略错位的表现。
  他们显然并没有意识到:在一个大工业化不断扩散的时代,海权的维持成本是相当高的。维持一支由于技术进步而不断汰换过时的战舰的现代化舰队,对 于任何一个国家的基本国力都将会构成沉重的负担,尤其是对于中小型国家而言更是如此。海军竞赛这种超级大国间的游戏是离不开人口、幅员和资源等基本要素支 撑的。换言之,随着后发大国的崛起以及技术的进步,那种建立在上一个时代的海权控制,其失效将只是一个很短的时间问题。
  19世纪末科学技术令人眼花缭乱的进步,对皇家海军舰艇的汰换,构成了前所未有的压力。一艘耗费大量心血设计和资金建造的新舰往往用不了10年 就会过时,甚至在船台上就已经不再先进。同一型先进军舰建造得越多,在今天意味着实力优势越大,但是也意味着在不久的将来因为技术的过失而承受的损失也越 大。费希尔上将推出的集当时先进技术于大成的无畏舰和战列巡洋舰,在使皇家海军超越对手的同时,也使得此前建造的绝大多数主力舰过时了,于是对手也能够在 新的技术起点上以相对平等的姿态与皇家海军展开技术竞争和造船竞赛。
  海权不是空中楼阁。葡萄牙人、西班牙人甚至荷兰人的历史早已经证明,光有钱和冒险精神,是无法维持一个强大的海权的。没有强大生产力支撑的庞大 海上贸易,奢谈世界第一的舰队是愚蠢的。一个现实的逻辑是:要想成为经济大国,未必需要立即谋求世界第一的海权,而对于一个世界第一的大国而言,拥有世界 第一的海权也就是必然的。
  也就是说,是帝国造就了海权,而不是海权造就了帝国。由于海权的竞争性决定了其并非一次性投资,维持一支足够保持海权的军事力量往往和建造这样 一支力量同样昂贵。因此实力对比的改变,意味着经济秩序的改变,更意味着海权的易手只不过是一个时间问题。这个游戏规则就如同牛顿定律一样,绝不会因上一 个老牌帝国的好恶而改变。
  19世纪末,高傲的英国人一觉醒来去而发现:“身上的衣服是的德国缝制的,少女们周末穿的漂亮披风上衣来自德国,更让人吃惊的是生活中许多东西 都产自德国,如孩子们的玩具、童话书、厨房用品、排水管、收音机、纸张、铅笔,就连周末歌剧院里上演的歌剧也是德国人创作的,演员无一例外都是德国人”。 当19世纪末德国迅速实现工业化后,英德之间最主要的矛盾就集中在了如何瓜分世界市场这个问题上了。随着价廉物美的德国产品如潮水般涌向了被英国占据已久 的世界市场,作为工业革命的发祥地,英国在后起之秀的竞争中,已经难保上风。
  19世纪末,英国在经济上对其它列强,尤其是以美国、德国为代表的新兴国家的优势正在日渐缩小,甚至逐渐消失。因此伴随着经济基础的改变,不管 有没有来自新兴国家的挑战,经济规模已经不再是世界第一的大英帝国,对于垄断海权的控制已日益感到力不从心。他显然并没有意识到,在一个大工业化不断扩散 的时代,海权的维持成本是相当高的。海军竞赛这种超级大国间的游戏是离不开人口、幅员和资源等基本要素支撑的。换言之,随着后发大国的崛起以及技术的进 步,那种建立在上一个时代的海权控制,其失效将只是一个很短的时间问题。
  在骄傲自大与恐惧多疑的驱使下,从19世纪末到20世纪初,英国在不断执行或酝酿各种规模恐怖的海军扩张计划,以图使任何潜在对手甚至连反抗的 机会都没有,用炮舰外交去“创造和平”。面对来自新兴国家海军军备的强劲竞争,两强标准甚至三强标准被相继提出。然而,科技的革命,使得大工业化时代的海 军竞赛是如此昂贵,以至于企图包打天下的大英帝国政府在19世纪末已经沦落得像一家快破产了的股份公司。即使如此,极力主张加强皇家海军优势的海军大臣塞 尔伯恩伯爵依然强调:“在我看来,这个国家所有的花销就在于维持它的银行信用和它的海军”。
  马汉的预言
  为了手段,不惜牺牲目标,哪怕国家破产也在所不惜。这种举国上下对海权的痴迷,已经使得海权成为了大英帝国的核心国家利益的化身,进而凌驾于了所有其他国家利益之上。
  为了阻止别人染指统治海洋这种暴利行当,英国人总是动不动将从德雷克到纳尔逊为代表的“军神”抬出来,以一种“天命所归”的架势将帝国海权的来 源神秘化,拒不承认经济基础对于海权的支撑。而向大家灌输一种作为“自在之物”形态存在的、本末倒置的“利维坦”式的海权观——将工业革命的伟大成果和资 本主义近代国家发展的驱动力归结于海权,似乎是海权创造了经济的基础,而不是经济提供了海权的基础。
  马汉的伟大之处在于发现了获得“可望而不可及”的海权或者挑战“天赋唯一”海权的技术途径,并将这种途径知识化、条理化,同时使之深入人心。海 权论关于大舰队决战是取得海权的关键途径的看法,正好使得后发国家囿于生产能力过剩的大工业寡头和苦于无力施展拳脚的自由主义者们找到了利益交集,因而在 包括英国在内的“文明世界”迅速传播。而威廉二世和他的海军部国务秘书提比茨元帅,则无疑是马汉学说最积极的运用者。他们作为后来者显著地挑战了大英帝国 那看似无懈可击的海权。或许,让威廉二世君臣遗臭万年的真正原因,恰恰在于就在战争爆发前,他们通过实践马汉的药方,曾经真的接近过成功——而这个药方正 是大英帝国的精英们所刻意隐瞒的。
  马汉在1912年11月前往欧洲旅行期间,曾经对英国海军大臣丘吉尔作过一番很理性的忠告:“英国政府事实上并没有装备足够的舰队。他被迫将 4/5的战列舰集中于大西洋,剩下的地中海舰队不足以维护帝国在重要水道的交通。与以往时代相目比,英国皇家海军的规模更显不足。—旦与德国发生战事,大 不到颠与德国在北海争夺的同时,就不得不把地中海委托给法国来控制”,“德国重工业的高速发展能够为德国海军的发展提供基础,而德国海军的迅速增张和建造 无畏舰的能力也在同时增加,大不列颠民族必须不带任何冲动和冷静地认识这种形势,因为—个意志不统一的国家,要同德国那样高效组织化的国家竞争将处于极大 的劣势”。
  马汉的本意或许是为了提醒丘吉尔转变对英德海军竞赛狂热推动的态度,但是丘吉尔显然是从相反的方向来理解的。他以更犀利的言辞去推动了英国上下 “统—思想”来对抗德皇威廉二世那个“军阀政府的威胁”,并且以更大的热情和一切道听途说、耸人听闻的题材去游说帝国政府,从捉襟见肘的财政中榨出更多英 镑去建造昂贵的无畏舰。幸好,第一次世界大战在两年后爆发了,否则很有可能要不了等到提尔比茨设定的英德舰队达到平衡的1920年,大不列颠的经济和政府 就被丘吉尔的大舰队“击沉”了。
  人无祖富贵
  毫无疑问,按照以丘吉尔为代表的英美官方说法,是德国主动挑起的海军竞赛恶化了英德关系,并最终导致了战争的爆发。
  于是乎,许多人以此为诫,不厌其烦地教导那些后起的大国一定要放弃对“海权”的热望,以免引起某些掌握海权的先发大国的猜忌和愤恨,重蹈德意志 第二帝国毁灭的覆辙。在他们眼中,似乎一个新兴的国家只要一旦企图染指被别的国家牢牢掌握的“海权”,就将像人类和平的公敌一样可怕并自取灭亡。在这些看 似言之凿凿的推断后面,隐藏着一个有意思的悖论,那就是在过去的一个世纪中,世界性的海权已经从大英帝国易手给了美国。既然“海权”的专利权是如此神圣不 可侵犯,那么为什么同是欧洲文明的子孙,盎格鲁一撒克逊民族之间的海权交易就可以,而别的国家却连想都不能想?
  当我们在历史的长河中放眼望去,不难发现永恒的海权是并不存在的。希腊舰队毁灭过更加强势的波斯帝国水军;斯巴达人的联盟打败过雅典这个爱琴海 的霸主并迫使其交出了舰队;装备“乌鸦”的罗马旱鸭子们将地中海从迦太基人手中夺走变成了罗马人的内湖;基督教世界拼凑的舰队彻底击败了奥斯曼帝国的强大 海军,英国舰队凭借火炮和飓风毁灭了西班牙的无敌舰队……
  在西方文明不到2600年的历史中,海权的易手曾经在波斯人、雅典人、斯巴达人、迦太基人、罗马人、维京人、葡萄牙人、阿拉伯人、西班牙人、荷兰人、英国人、美国人,甚至于在阿拉伯海盗、私掠船长之间远不止一次地发生过。
  屈指算来,今天的那个“冠军”宝座被美国占有也不过才一个甲子而已!那凭什么对我们而言,“被”看到的,就只能是那些失败的挑战者们呢?不光如 此,甚至那些从拿破仑到戈尔什科夫在内的所有功亏一篑的挑战者,也都无一例外地被贴上了“文明世界的威胁者”或者“邪恶帝国代理人”的标签?所谓欲加之 罪,何患无辞!
  不列颠造神运动
  不管你是否愿意承认,一个残酷的事实在最近6个世纪以来的历史,是英语民族主导下的历史。
  英伦三岛上数千万盎格鲁-萨克森人近乎于恐怖的生产力大爆炸,吸引了全世界绝大多数民族的注意力。这种技术上的绝对优势,在将落后国家的人们震 惊的目瞪口呆的同时,使得相当一批非英语民族的精英分子对不列颠文明无比艳羡顶礼膜拜之余,开始了自觉不自觉的接受了关于这种“神迹”诞生的各种原因,没 有批判,没有扬弃。
  而英国人则以1534年通过《至尊法案》“奉英国国王为英国教会所在地上唯一最高首脑”,从而使英国圣公会皈依于罗马教廷的整个基督教世界决裂 的大无畏精神,自命权威,自创规则,为帝国的各个领域蒙上了“授命于上帝”的神圣法衣。于是不知不觉间,许多人也就成为了这种莫名其妙的神圣性的奴隶。不 列颠海权经过这样的洗礼,其来龙去脉和真实意义也就被彻底神秘化了。
  这样,一个“侵略有理,反抗有罪”的思维定势就诞生了,因为英国人是追回、进步、正义的化身,所以凡事英国人宣扬的,即使会损害我们的利益,也 是神圣不可侵犯的;凡是英国人反对的或者企图挑战英国人优势的实力与思潮,哪怕是我们的兄弟或者我们千百年来遵循信仰的,也是落后、愚昧、甚至于反动、邪 恶的。从这个角度看,俨然“天下共主”的英国人控制世界意识形态的能力,特别是对其他民族的思维引导力,可能远远超出了其作为世界共产的生存力,而在人类 社会长盛不衰。
  成功者可能有千百种特质,很难说哪一种特质与成功的结果之间,一定是因果关系。事实上“时序关系”也是大量存在的。这就如同在一个即将下雨的上 午出门带伞绝不是中午降下倾盆大雨的原因一样。两个时间在时间上的先后或者伴随,也有可能是没有直接关联的。最近几个世纪以来,英国及其崇拜者们一直有一 个古怪的逻辑:那就是因为世界第一强国都拥有海权,因此只有去的海权才能成为世界第一强国。进一步,国家的竞争,归根结底就成为了海权的竞争;只要组织别 人拥有海权,就可以保住自己的世界第一强国地位不动摇。马汉的错误,就在于通过相当系统而“科学”地归纳总结,过分夸大了海权对于历史和经济的影响。因 此,其海权论一经问世,马上就成为了英国人对自己必须独霸海权的可笑逻辑的有力证明。
  这个逻辑的可笑之处就在于:将海权从结果误认为误认为成了原因!
  美国人的“临界点”
  
  那些反复强调美国也是靠海权发家的人们,显然忘记了一个基本事实,当门罗提出“美洲是美洲人的美洲”时,美国并没有强大的海军。到1890年,当美国 已经凭借巨大的内部市场雄踞资本主义世界经济规模前五位的同时,美国海军的排名仅为世界第十二位。以至于在1 879年的南美太平洋战争中,凭借两艘新式英制铁甲舰而腰板强硬的智利海军,毫不客气地告诉企图扮演和事佬的美国海军使团,并通过他们告诉美国政府:如果 要管闲事,就把他和他的舰队送到海底去!——其实,这样的话在1866的西班牙对智利的战争中,企图调解的美国海军就已经从西班牙海军的门多茨o努内茨少 将那里听到过一次了。
  美国之所以能够挑战英国,并且最终成功的关键原因,就在于其庞大的国内市场和邻近的拉美市场提供了经济成长的“缓冲地带”,这种避免过早地与原 有的大国格局发生冲突从而引发危机的“韬光养晦”能力,恰恰是德国这类国内市场狭小的欧洲帝国所不具备的。在大工业化时代,美国凭借庞大的内部市场,在较 少依靠海外摊薄成本的情况下,取得了规模优势,同时又通过事实上的“韬光养晦”从而避免了过早与其它老牌优势国家发生冲突。但是,当这类国家的国内市场、 国内资源已经无法满足其经济成长需要的时候,寻求在“先发国家”把持下的世界市场中的平等贸易权力,甚至是排他性的垄断,也就是自然而然的事情了。
  卡内基曾经在他那本《成功的民主》中吹嘘:美国的强大实力并不是靠军事力量——“感谢上帝,美国的海军几乎等于零”。但这并不妨碍这位有点和平主义倾向的钢铁大王投入到为列强们的海军竞赛提供枪炮和装甲的“伟大事业”中去。
  美国在20世纪前对海权的淡定,并不是由于什么和平主义,而主要在于国内市场优先的经济形势,以及南北战争后在独特的联邦权力架构下急剧萎缩的 海军实力。无论是在1873年11月西班牙捕获美国籍“福吉纳斯”号偷渡船并处死共上53名美国籍雇佣兵事件,或者是在1891年“巴尔的摩”号巡洋舰在 停靠智利瓦尔帕莱索港期间发生的导致2名美国水兵死亡、1 8人受伤的斗殴事件中,美国之所以保持“克制”,最主要的原因还是在于面对强敌时实力不济或者“成本与收益不成比例”。实际上,只要有机会,美国是从来不 会放弃凭借海权扩张的机会的。例如对于战略意义的巴拿马,美国海军就曾经在1846-1903年间,至少7次直接干涉。马汉本人就曾直接受命于美国国务 卿,指挥军舰对中美洲国家进行过炮舰外交。
  19世纪末,当美国觉得非进入世界市场不可的时候,其工业生产巨大的规模优势所表现出的战争潜力,已经使主要对手在成本上望尘莫及了。
  其实,对于一个国内市场足够大、人口足够多、资源足够丰富的国家而言,在相当长的一段经济发展期中,其对于海外市场或者海权的依赖,的确远没有 像英国那种岛国迫切。德国之所以能够在基本没有海外殖民地的情况下,仅经过了较短时间,就形成了超越英国的工业力量,一个重要原因就在于1871年实现帝 国统一后形成的高效规模优势。这种在成本上的优势使得传统先行国家如英、法在竞争中感到难以招架。
  但是随着社会生产的扩张,德国在随后的发展中也遇到了英、法曾经遭遇到的那种规模瓶颈。突破这个瓶颈,引导经济走向下一个触发从量变到质变的规 模“临界点”,也就成为在俾斯麦之后,威廉二世君臣渴望“阳光下的地盘”的经济动因。从德国垄断资本家们的角度看,德国“过早地”卷入到与英法争夺世界市 场的争斗实属“逼不得已的无奈之举”。实际上,对于任何大国而言,无论外向型经济能够多么迅速地推动经济发展,一个无法回避的问题都是:一旦形成了足以挑 战先行国家的实力,该如何面对来自传统帝国的致命反击?
  老海盗与丛林法
  一个看似有道理的观点认为:“对于美国来说,英国是一个占有巨大帝国赃物的退隐的老海盗,比起刚刚开始海盗生涯的年轻的德国海盗来说,更倾向于和美国和平相处,因为德国只有靠把目前的世界赃物分配搞得稀烂,他才有希望赢得阳光下的地盘”。
  可是这未能说明为什么第一次世界大战前后,美国对世界格局的改变远比德国为甚,但是战争却没有在美国和英国之间爆发?同样,苏联红海军在戈尔什 科夫海军元帅的带领下,一度成为了令北约胆寒的强劲对手,严重动摇了美国所代表的整个资本主义世界的海权,但是这两个国家却也没有爆发战争?
  纵观19世纪末的历史,不难发现,美国和英国之间的矛盾一直不断。1895年,当英国与委内瑞拉发生边界纠纷时,克利夫兰总统就以门罗主义为依 据,公开向英国发出了强硬的信号,迫使正被其他殖民地事务搞得焦头烂额的英国接受国际仲裁。英国此后不得不通过缔结条约去承认美国在自己鞭长莫及的加勒比 海和美洲的海权,以换取将主力舰队收缩于欧洲水域对付法国、德国以及俄国。
  对于这个同样说英语的“表弟”,英国也并非是将海权拱手奉送。1919年,在第一次世界大战刚结束不久后召开的巴黎和会上,由于美国在军备问题 上的咄咄逼人,倍感威胁的英国已经扬言“大不列颠将以它全部心血保持一致优于美国或任何其他国家的海军”。对此,威尔逊总统的私人顾问立即警告他的总统: “美英两国的关系开始具有英国和德国在战前关系的性质”。美英之间之所以没有爆发像英德那样你死我活的战争,与血缘或者上帝并没有关系,而是由于大英帝国 在被第一次世界大战搞得精疲力竭的同时,已经认识到了和一个战争潜力远远超过自己的对手进行全面战争的危害!这也就是英国在此后被迫坐到谈判桌前与美国和 日本分享海权的原因。可见,问题的关键可能在于实力,而不是所谓的“是非”!
  这一点,凯恩斯的观点颇有见地。在这位经济学巨匠曾对一战这样评论道“权力的政治是无可避免的,关于这一次战争和其目的,似乎并没有什么新东西可以学习。还是和过去的每一个世纪中一样,英国已经毁灭了一个贸易上的敌人”。
  比凯恩斯稍早一些的另一位英国显贵,阿瑟o贝尔福在一次和美国同行的谈话中说得就更明白了:“在德国建造了许多船只和抢夺了我们的商业之后,而 我们还找不到一条理由来向德国宣战。”这就是问题的关键!贝尔福之所以认为需要和德国人打一仗,就是因为德国人的崛起将降低英国人的生活水准,而英国人认 为自己恰好拥有足够的暴力去阻止这一自然进程,这就足够了。所谓挑起海军竞赛或者企图染指海权,只不过是众多用来证明战争合法性的无厘头证据中看来较为有 道理的一个而已。就如同某个或者某几个文明国家宣称:因为另一个国家在研制这些国家早已拥有的大规模杀伤性武器危害了人类和平,就必须去占领那个国家推翻 那个政权一样。只许正义的我有,不许邪恶的你想!这个经不起推敲的道义合法性的基础,不过是丛林法则在工业社会中的滥瓠罢了。
  卧榻之侧
  在那个帝国主义横行的时代,所谓战争,不过是国家之间争夺利益的惯常工具而已。在帝国精英们看来,对于包括中国人在内的所有“野蛮人和半开化的 野蛮人”,机关枪和炮舰要比任何“耐心的外交辞令”有效得多。他们笃信的是,机关枪里出和平,炮舰到处有平等。这种充满十字军式的宗教狂热般的圣战什么时 候爆发,在哪里爆发,以谁为对象爆发,关键在于“效益”相对于成本是否足够大,是非曲直并不重要——有个借口就足够了。
  然而,第一次世界大战的不同之处在于,它使得那些视战争为儿戏的显贵们、需要用爱国主义提升支持率的政客们以及被“主流舆论”引导得近乎癫狂的公众们,第一次意识到了,在一个大工业化和民族主义涌动的时代,工业化或者半工业化国家集团间大规模全面战争的可怕之处。
  战争一经发动就不再是皇帝、总统、首相、将军们这些民族精英们可以左右的,舆论和民众的合力,会将任何“体面和平的奢望”撕得粉碎。那样的战争 不再是军队之间局限于战线前沿的有限碰撞,而将是贯穿整个民族所有生存空间的全方位的厮杀。战争不再是短暂的,而将是空前持久,直到筋疲力竭的一方倒下为 止。
  在这种战争中,对最开始就投入交战的双方而言,失败一方固然是惨败,但胜利一方也将是元气大伤的惨胜。真正的胜者必然是在战争半途加入的那个“渔翁”,而一个元气大伤的国家,除了暴风骤雨般的革命以外,绝没有独自退出战争的可能。
  在最近200年中,大英帝国及其大西洋对岸那位盟友,一直自命为世界秩序的维护者。他们居高临下地将每一位竞争者异化为挑战者,再将破坏世界和 平的道德枷锁强加到这个不知天高地厚的竞争者头上。这样,一场哪怕最肮脏的战争,就会立刻如同圣战一般焕发出令善良的人们如痴如醉的光辉。
  
  回到问题的起点,难道真的是马汉的海权论引发的海权竞赛点燃了世界大战的烽火的吗?如果答案是肯定的话,那么一个原初的问题就是:为什么盎格鲁-撒克逊国家拥有海权就是合法的,而另一个国家企图染指这样的权利就是邪恶的战争企图?
  如果大英帝国离开了海权就不能维护其经济利益,那么凭什么断定别的国家就可以在没有海外市场的环境中生存呢?
  在大英帝国,其第一海务大臣费希尔在1904年上任时可以喊出:“抢先打击、狠狠打击、随处打击”。这不会被认为是战争狂言,而德意志第二帝国 的海军大臣提尔比茨,仅仅谋求“我们至少还能在面对英国的防御战中具有军事机会”的努力,就会被视作是对热爱和平的大英帝国的冒犯?要知道,实际上在 1912年德国在海军方面的投入已经显著低于英国,就连俄国1913-1914年在海军上的投入也要多于德国。可见,这绝不是一个简单的傲慢或者双重标准 问题。
  马汉的海权学说的真正精华之处,或许并不在于列举了海权对于大英帝国的重要性,而在于使得那个时代的人们,清晰地认识到了控制海洋对于一个贸易工业大国的极端重要性。
  按照马汉的观点:海军的作用是“提供和保护交通线,并且解决必然会从商业竞争中爆发的冲突,从而确保剩余产品进入外部市场”。很明显,大英帝国 并不愿意和其他人分享这种海权。不是因为这种权利会对这个岛国的国家安全造成多大威胁,而是在于这样有可能会挑战英国在海外市场中“吃独食”的垄断利益。
  按照西方经济学中的效率原则,一个社会一旦达到了所有资源已经实现了最优配置了的“帕累托”最优,就意味着你之所得,一定会等于我之所失。在这 样的社会中,我要多,你就得少!同理,我要不能少,你就决不能多!垄断的真谛,就是干掉一切竞争者和潜在的觊觎者。垄断是目的,至于独一无二的海权不过是 一个工具而已。
  所谓战争不过是消灭竞争对手或者是潜在竞争对手的有效方式而已。在第一次世界大战爆发前,英国人一直认为凭借其强大的皇家海军所掌握的绝对海 权,对竞争对手的战争将是短暂而有效的。这种廉价的选择,使得率先进入无畏舰时代的英国皇家海军,相当热衷于对敌国进行先发制人的打击,将敌人的海岸线作 为战争的前线。
  因此,自老、小皮特这对父子首相以来,这个海洋帝国的国策就是“将英国的安全与耗尽敌人的力量完全等同起来”。
  尼伯龙根的屠龙刀
  很不幸的是,制止这种战争乐趣的途径被马汉发现了,那就是建造一支足以和英国皇家海军进行正面的舰队决战、并有能力给敌人以难以承受打击的舰 队,将是在那个时代保持和平的有效途径。这个理论被德国海军国务秘书提尔比茨发展为了“风险舰队”理论,并被德国皇帝威廉二世所接受和加以实践。
  尽管德意志第二帝国在建立自己的海权方面取得了卓越的成就,然而1914年6月15日奥匈帝国皇储斐迪南大公夫妇在萨拉热窝的遇刺改变了这一 切。如同凯恩斯所断言的那样,那是一个天赐良机。这也就是为什么当时的英国外交大臣格雷子爵非但不积极进行和平斡旋,反而一直以暧味不清的信号向包括德国 在内的各国发出含糊信息的根本症结所在。大英帝国终于得以冠冕堂皇地发动一场毁灭潜在竞争对手的战争。然而她所没能预料到的却是,这场战争竟然是她正式向 另一个强国移交海权的开始。
  在“婴儿杀手”希佩尔率领下的德国战列巡洋舰队的隆隆炮声中,在齐伯林飞艇投下的炸弹声中,英国第一次体会到了被另一个工业化国家打上家门的心 情——其实这正是此前英国军舰和士兵经常对别人干的。而那些一贯乐于以坐着说话不腰痛的心态指责在日德兰海战中杰里科没有对舍尔穷追不舍的书斋勇士们,可 能永远都体会不到皇家海军最精锐的3艘战列巡洋舰被“北海鸭”转瞬间送入海底,给肩负战略责任的大舰队司令的震撼。干掉敌人或许是可能的,但是如果自己的 舰队也毁灭了,甚至被渴望同归于尽的绝望对手打得元气大伤,那么这个帝国就完蛋了。这一幕在1918年11月,险些因为企图率领德国公海舰队全部主力舰孤 注一掷打上门来的希佩尔,而变成现实。实际上这正是1914年8月战争刚刚爆发时,提尔比茨所全力主张的。
  这就是大英帝国的怪圈,为了帝国,必须阻止别人拥有海权。可是到头来却发现,这个代价竟然是帝国的毁灭!那么,到底是海权为了帝国,还是帝国为了海权呢?这个关键的问题直到第一次世界大战之前,英国人是不明白的。
  结果,英国人用自己的惨胜,证明了马汉的理论,也证明了提尔比茨和威廉二世的风险舰队理论的正确性。尽管在1918年英国拘禁了德国公海舰队几 乎全部主力军舰和潜艇,但是不管德国海军主力是否选择了自沉,大英帝国的命运也是注定了的。虽然英国本土并没有受到明显的破坏,然而帝国的精锐和国力已经 在那场轻易爆发的战争中损失殆尽了。英国人用一场惨烈的自杀式战争毁灭了帝国的挑战者,也榨干了自己。
  蛮横的代价抑或冲动的惩罚
  当大英帝国筋疲力竭地坐在谈判桌前,等待她的却并不是胜利者的盛宴。那是一个地地道道的烂摊子,威廉德国的毁灭为俄国革命的火种在西欧的传播提 供了足够的柴草,若不是红军在华沙战役中出人意料地失败于了毕苏斯基指挥下的波兰军队,恐怕一场与海啸般袭来的苏维埃狂飙搏斗的全欧战争又将再度爆发。
  另一方面,在大西洋彼岸,一个新兴的大国已经狰狞毕露,以势不可挡的气魄在昔日霸主的禁脔颐指气使、指手画脚。在美利坚面前,如同黔之驴般的不 列颠已经底牌尽显,仅仅是靠帝国的余威勉力支撑。未来世界的霸主已经注定将要在苏联与美国之间产生,而英国已经不再是那只举足轻重的“离岸平衡手”!
  尽管经历了“20年的休战”,但是大英帝国依旧没能恢复昔日的辉煌。而此时的英国政治家们在饱尝了第一次世界大战这杯自己酿造的苦酒之后,很快 又从一个极端走到了另一个极端。在削弱德国失败后,面对咄咄逼人的法西斯,张伯伦首相选择了自以为成本很低的绥靖政策。无论是牺牲别国、还是祸水东引,结 果都是一样的,那就是将自己已经无力再战的状态,赤裸裸地呈现在了野心勃勃的轴心国面前。这一次,姑息换来的无疑是更大的战争。大英帝国已经到了要用若干 重要基地 99年的租用权,交换50艘美国在第一次世界大战期间建造的老式驱逐舰的地步了。
  与上一次世界大战不同的是,第二次世界大战更为惨烈,对英国本土和欧洲这个传统中心地带的毁灭更为彻底。这场战争不但使得大英帝国最终拱手交出 了海权,并且促成了整个殖民帝国的解体。布雷顿森林体系使得美元取代了风雨飘摇的英镑成为了国际货币。马歇尔计划使得整个欧洲成为了美国倾销商品的市场, 并最终使得欧洲的工业体系与美国接轨。关贸总协定使得原来被殖民帝国牢牢控制的各个市场,真正对美国实现了“门户开放”。联合国安理会体制的建立,使英国 在政治上成为了美国的附庸。而北大西洋公约组织的集体安全机制,则使得英国皇家海军从一支纵横四海的世界级武装力量,矮化为了美国海军统领下的专业化反潜 打手。
  在新兴的美利坚海权面前,沦为配角的大英帝国已经基本没有了独立性可言。1956年的苏伊士运河危机已经明确地告诉了每一位唐宁街的主人:与美国合作,还能分一杯羹。继续抗争,无异于以卵击石。
  经济基础的枯竭,最终使得英国在战后全面执行政治上追随美国、经济上依附美国、军事上仰仗美国的一边倒的国策60多年后,“进化”成了一支可能 不再拥有航母、最终水面作战力量与葡萄牙相类似的欧洲二流海军。以至于在压缩预算的沉重压力下不得不考虑放弃一艘或全部伊丽莎白女王级航母,转而与法国 “共用”航母。而具有讽刺意义的是,潜在共用对象却是“戴高乐”号——以一位曾经参加二战并最终带领法国退出北约军事框架的法兰西总统命名的核动力航母。
  大英帝国之所以会以这种尴尬的形式移交海权,并不是因为她真的和大洋对面的那个国家袍泽情深,而是因为“国运使然”!那个新兴的国家具有的战争 潜力和海军实力,早在19世纪末就足以令这个已经被布尔战争和苏丹的马赫迪起义搞得焦头烂额的世界帝国暗自战栗。而大英帝国不是执行休养生息的政策,却依 旧企图将列强成集的欧洲和北非或者近东作为不容他人染指的圣地。
  
  这就是为什么,尽管老罗斯福派出大白舰队环游世界也没有激起英国人的强烈反应,而不知道英法签订了密约的德国人仅仅是派了一条炮舰到阿加迪尔“保护侨 民” 就足以引发伦敦发出歇斯底里的战争恫吓的原因。这也就是为什么,尽管冷战时期的红海军威胁了英国的海权,但是英国却并没有对苏联发动战争的原因是那个国家 拥有核武器,也就是毁灭这个国家的终极武器!于是,在互相毁灭的阴影下,英国人学会了与对手分享海权。欧洲获得了恐怖的但却实实在在的和平。
  工具与目标的辩证法
  海权不是自在之物。海权不过是服务国家利益的工具而已。如果说陆权的主要任务是保证一个国家不受侵略的话,那么海权在此基础之上进一步延伸出了使一个国家的商业利益以及至关重要的“发展权”不受侵犯的功能。
  威廉二世对海权的兴趣不在于对大型战舰的虚荣;更不是真的如某些人猜想的那样,是出于对其英国母亲所给予的不快乐的童年的报复。他追求的是将他那个日耳曼国家与世界帝国联系在一起的国家战略。海权不过是实现这个战略的工具而已。
  同样,对于大英帝国而言,消灭这样一支海军,并不是由于他对皇家海军的舰队或者英国的海外利益,甚至本土真的构成了什么威胁,而是既出于维护其在世界市场中的垄断地位——商业利益的考虑;更出于消灭可能直接威胁自身在国际秩序中地位的现实竞争者的考虑。
  那些脱离国家利益去谈论海权,或者将一战的起源归结于德国海军崛起的人们,显然没有充分地意识到,自德国成为后发大国开始挑战英国的海外市场和 英国主导下的国际秩序的那一刻起,这两个国家之间的结构性竞争就已经埋下了战争的种子。至于这颗撒旦之苗什么时候能够结出和平之果,就不再是取决于任何一 方的善良愿望,而主要是由什么时候能够形成那个恐怖的平衡,并使彼此能够清醒地认识到这一点。在那个核武器或者其他大规模杀伤性武器还没有诞生的年代,威 廉二世和提比茨扩张德国海军的初衷就在于此。
  其实两次世界大战及其之前的两个帝国间的海权较量对于今天的读者而言,最为重要的并不是在于告诉我们一个有效的海权对于一个后发大国的经济崛起 如何重要,对于她的海外市场如何重要,而是在于让我们清醒地了解,经济基础并不是决定国家崛起能否成功的唯一因素。一旦这个后发国家的崛起对世界格局将产 生巨大的改变,那么防止这个进程被原有格局的主导者,也就是先发国家,从外部强行轻易打断,就成为了这个后发国家所必须时刻关心的问题。从这个角度出发, 就不难理解,为什么二战后新独立的几个大国总是不遗余力地企图撑起一把属于自己的“核保护伞”。
  在一个全球化的时代,一个国家的安全早已不只限于领土、领海、领空这样的狭义的边疆体系。有地理边疆、安全边疆、利益边疆、影响边疆这样的现代 边疆体系所构的国家安全考量已经成为了现代国家战略的基点。而这个时代的海权,同其他形式一样,不过是维护这种国家利益的政策工具的体现罢了。如同宗教神 学般抽象地去大谈海军与海外市场的经济利益联系,并论证这种联系的因果必然性,或者以神秘主义的宿命论为立足点去生造什么利维坦与波希墨特之间的争斗,抑 或夸大心脏地带与边缘地带、新大陆之间的冲突必然性,是没有意义的。
  为了国家的现实需要而追求海权,与为了某种偏好而追求海权的根本区别就在于,究竟谁是目的:海权本身?还是国家的需要。
  大英帝国的战略错位
  一个世纪前,自闭于对昔日辉煌与光荣的陶醉和用想当然的思维勾画出的国际格局中的英国式海权思维的问题,在于“战略错位”的同时,缺乏对于此消 彼长的国际力量对比的清醒认识!更为深刻的背景,或许还在于对于经济基础与国家暴力关系的误读。结果,自然是在不断加码的战略压力下,踏上了自我崩溃的道 路。
  进入20世纪以后,以综合国力为核心的系统对抗的时代,已经取代了将单个人的主观能动性发挥到了极致的、充满浪漫的英雄主义色彩的德雷克-纳尔 逊时代。名将的成就越来越依靠的是物质基础,而不单单是对手的怯懦和愚蠢。时代已经改变,大英帝国却拒不承认现实,不明白螳臂当车的悲剧:有些东西是挡不 住的!这一点,在另一个今天的霸主身上也日益表现为了“显性遗传性状”。
  在马汉去世之后的一个世纪里,海权在西方及其在东方的代理人们那里,依然被有意无意地“升华”为了某种不容盎格鲁-撒克逊民族,也就是自己以外 的任何民族染指的自在之物。而和平,不过是这个自在之物可以随时抛弃的义肢而已。似乎为了维护海权,可以随时牺牲和平这样一个终极目标。因此,德国人的例 子被用来告诉我们:“走出国门和由此产生的海权需求对和平和自身的安全是多么的危险”。这种看法显然甚为偏颇。难道一个漠视海权的国家,或者按照西方所推 崇备至的俾斯麦所强调的可以完全忽视海军发展的国家,就会比一个足以挑战世界海权冠军的国家更为安全吗?答案当然是否定的。
  在西方的历史长河中,迦太基式的和平对许多帝国主义霸权的拥趸们来说,一直散发着强烈的诱惑。问题是交出了全部舰队的迦太基,也并没有逃脱被罗 马人彻底灭亡的命运,可见,一个国家的和平并不是仅凭善良的愿望就能得到的。对那些坚信“强权就是公理”的人们来说,“代价”是唯一可以让他们变得不那么 狂妄和妒忌的有效镇定剂。
  拥有海军不是问题!
  问鼎海权也不是问题!
  问题在于这个国家的崛起对原有的世界格局会产生怎样的影响,以及这个崛起的海权将使潜在的对手为了毁灭它而付出怎样的代价!
  失灵的咒语
  1901年3月12日,马汉在致当时的美国副总统西奥多o罗斯福的信中,预见了一个比俄国更为凶险的威胁——中国:“中国如此庞大的4亿人口一旦集中到一个高效政治组织中去,以现代器具装备起来,并且囿于对他来说已经是狭窄的领土之内,对此很难泰然思之”。
  对此,马汉开出的药方并非是显示军事力量,而是更多地依靠和平的商业渗透,将亚洲人民带入基督教国家大家庭圈内:“我们可以指望他们遵循基督教 的道德和精神观念,超过其拥有在价值份量上的物质福利。”说白就是要中国拱手让出与自己的实力和对世界的贡献相称的权利,去为那个世界充当自我污染、自我 消耗、自我毁灭的现代农奴。
  一个世纪过去了,尽管我们中的许多人对中国人是否生活在一个高效的政治组织中争论不休,但是超过13亿人正在被现代化的工具与技术“武装”起来 却是不争的事实。而那些自命为原有世界秩序的维护者们对此“很难泰然处之”,同样也是事实。他们会运用怎样的 “基督教的道德和精神观念”来整合我们?这些道德和精神观念又将对我们产生什么样的影响?带来什么样的结果呢?
  或许,关于海权的戒律就是其中颇有成效的一个:甘心做奴隶或者选择继续沉睡吧!当中国人开始考虑跨出国门,由英语世界编纂整理、为非英语世界中 的许多精英所接受并广为传播示众的威廉二世挑战英国海权导致亡国巨祸的悲惨故事,就会在我们耳边如同催眠咒语般响起。这种迫使人们在精神层面臣服的力量是 如此之大,以至于能够压倒几乎所有胆敢呛声的微弱质疑。
  古希腊的政治历史学家修昔底德在《伯罗奔尼撒战争史》中曾经写道:“在人类的历史中,未来虽然不一定是过去的重演,但同过去总是很相像的”。如 同两次大战之后的英国由于领悟到了代价的高昂,而不再执着于对后来圈圈竞争者们的保持偏执狂版的仇恨一样;或许同样处在“基督教国家大家庭圈内”也同样遵 循“基督教的道德和精神观念”的德意志第二帝国构建自身海权的那段曲折历史,也能带给另一个新兴的大国以有益的启示.

在linux vps上搭建基于python的静态网站程序aam

pip install aam
mkdir aam-site
cd aam-site
aam init (这个是初始化命令,会在当前目录下初始化网站的目录结构,生成一些文件夹和文件)


root@as3:~/aam-site# ls
config.ini  deploy  pages
root@as3:~/aam-site# ls pages
root@as3:~/aam-site# ls deploy
root@as3:~/aam-site# cd pages
root@as3:~/aam-site/pages# nano index.md
内容为:
title: Welcome
date: 2014.6.27
type: home
----

Weclome to Aam!

root@as3:~/aam-site/pages# nano hello.md
内容为:
title: Hello World
date: 2014.6.27
description: The first page
type: page
----

Hello, World!

root@as3:~/aam-site/pages# cd ..
root@as3:~/aam-site# nano config.ini
(把site_url的值改为你的域名地址,我的为http://aam.brite.biz.st)
root@as3:~/aam-site# aam build (这个就是生成/更新静态网站的命令)
root@as3:~/aam-site# cd deploy
root@as3:~/aam-site/deploy# ls
hello.html  Home.html  static
(有Home.html,可见~/aam-site/deploy/就是静态网站的根目录.为方便起见,可把Home.html重命名为index.html)
root@as3:~/aam-site/deploy# nohup Rwebserver 32193 > /dev/null &
访问http://as3.brite.biz:32193/index.html即可看到网站效果。

发贴方法:
root@as3:~/aam-site/deploy# cd ../pages
root@as3:~/aam-site/pages# nano test1.md
内容为:
title: test1
date: 2014.6.27
description: page test1
type: page
----

这是测试1.


然后,
root@as3:~/aam-site/pages# cd ..
root@as3:~/aam-site# aam build (这个就是生成/更新静态网站的命令.每次运行此命令后,所生成的deploy目录的首页文件仍然是Home.html,需记得把Home.html重命名为index.html!)

演示站点:http://as3.brite.biz:32193/index.html,http://aam.brite.biz.st
项目地址:https://github.com/JmPotato/Aam

Building static sites in Clojure with Stasis


Stasis is a static site toolkit for Clojure. Unlike pretty much every other static site generator, though, it is not an "opinionated framework", or packed full with flavor-of-the-month templating languages and whatnot. It is just a few functions that helps with creating websites that can be hosted as static files, and developed against a live server (enabling layouts, and various dynamic features to generate the pages). As its Readme states; there are no batteries included. This post will take you through creating your first Stasis site, and serving it with super-optimized frontend assets, courtesy of Optimus. Both Stasis and Optimus are written by my good friend and colleague, Mr. Emacs Wizard, Magnar Sveen.
The source code for this post can be found on GitHub. As I plan to evolve the code to become my new blog (eventually), you should look for the blog-post tag, which represents the code as per this post.

Who is this for?

This post may interest anyone looking to set up a static site of some sort. The range of sites that can be successfully developed as static sites are bigger than you might think. While Stasis is a Clojure tool, only a very basic understanding of Lisp should be necessary to follow along.
I'm hoping this post will not only show you the power of Clojure and Stasis for building static web sites, but also give you a good introduction to some very useful Clojure libraries. Maybe even to Clojure itself. In particular, I will discuss using these libraries: Stasis, Optimus, enlive, hiccup, cegdown, clygments and even write some tests with Midje.

Getting set up

First things first, let's get a project set up to serve our frontpage. If you've never worked with Clojure, install Leiningen. Now create your project:
lein new cjohansen-no
cd cjohansen-no
That creates an empty project for you. I will use this post to start the new code base for my blog. You might want to give yours a different name. Open project.clj, and add Stasis as a dependency. While you're at it, add a description and tune the license to your desires. When you're done, it should look something like this:
(defproject cjohansen-no "0.1.0-SNAPSHOT"
  :description "cjohansen.no source code"
  :url "http://cjohansen.no"
  :license {:name "BSD 2 Clause"
            :url "http://opensource.org/licenses/BSD-2-Clause"}
  :dependencies [[org.clojure/clojure "1.5.1"]
                 [stasis "1.0.0"]])
Now add a truly static page (i.e. a file). Create and open src/cjohansen_no/web.clj. This is the namespace we will use to define our pages. Put the following content in it:
(ns cjohansen-no.web
  (:require [stasis.core :as stasis]))

(defn get-pages []
  (stasis/slurp-directory "resources/public" #".*\.(html|css|js)$"))
#"..." is Clojure syntax for regular expressions.
(Note that the namespace is called cjohansen-no, while the directory was called cjohansen_no. If you misspell either of these, Clojure will bark at you with a rather unfriendly message saying it can't find the namespace. Remember to double check file/directory names and namespaces).
The web.clj file pulls in stasis under the local name stasis, and defines one function. Stasis will expect to receive a map of url => content defining pages, so this is what get-pages does. The content can be a function, in which case it will be only be called when Stasis needs to serve this particular page. This lazy loading becomes useful if your site is big enough for it to become slow by loading everything in one go. For now, we'll not worry about it.
slurp-directory is a Stasis function that creates a map of files in a directory (recursively). Every file in the subtree that matches the provided regular expression (i.e. every html, css and js file under resources/public) will be included. The keys in the map (e.g. the URLs), will be the file path relative to resources/public. For example, resources/public/index.html will be served as /index.html.
Put the following in resources/public/index.html:
<!DOCTYPE html>
<html>
  <head>
    <title>My blog</title>
  </head>
  <body>
    <h1>My blog</h1>
    <p>
      Welcome to it.
    </p>
  </body>
</html>
As Stasis is a "no batteries included" framework, we need to do some work to either export the static site or view it live through a web server. As the file is already static, we'll set up the live server first. Update project.clj to pull in a couple of dependencies and configure a Leiningen plugin:
(defproject cjohansen-no "0.1.0-SNAPSHOT"
  :description "cjohansen.no source code"
  :url "http://cjohansen.no"
  :license {:name "BSD 2 Clause"
            :url "http://opensource.org/licenses/BSD-2-Clause"}
  :dependencies [[org.clojure/clojure "1.5.1"]
                 [stasis "1.0.0"]
                 [ring "1.2.1"]]
  :ring {:handler cjohansen-no.web/app}
  :profiles {:dev {:plugins [[lein-ring "0.8.10"]]}})
Ring is the defacto HTTP toolkit for Clojure, and can be compared to Python's WSGI or Ruby's Rack. We have added it as a dependency, and set up the app function in the cjohansen-no.web namespace (e.g. the file we created earlier) to be the entry-point for the web server. The profile configuration loads some convenient Leiningen tasks for the development profile (which is also the default profile).
A web application in Ring is just a function that receives as its only argument a request hash and returns a map like the following:
{:status 200
 :headers {"Content-Type" "text/html"}
 :body "Hello World"}
Stasis provides the serve-pages function to help us produce this response. It returns a function that will look at the request and find the corresponding page in our map. Add the app function to the src/cjohansen-no/web.clj file:
(def app (stasis/serve-pages get-pages))
Run the server:
lein ring server
This will pop up a browser displaying your static HTML file in all its naked glory.

Adding templating

Next we will add a page that is split between the content/body of the page and the wrapping layout. The layout will be shared by many files, so applying it in one central place saves us some work.
Add a partial page to resources/partials/about.html
<h1>About this site</h1>
<p>
  It is a web page.
</p>
Rather than using HTML manually to create the layout, we will use the popular Clojure templating library called Hiccup. Hiccup allows us to express HTML in a more compact form by using vectors, keywords and maps. It is best illustrated with an example. Add Hiccup to project.clj:
:dependencies [[org.clojure/clojure "1.5.1"]
               [stasis "1.0.0"]
               [ring "1.2.1"]
               [hiccup "1.0.5"]] ;; Like so
Alter the namespace form in src/cjohansen_no/web.clj to require the html5 function from Hiccup:
(ns cjohansen-no.web
  (:require [hiccup.page :refer [html5]]
            [stasis.core :as stasis]))
Add the following function to the same file:
(defn layout-page [page]
  (html5
   [:head
    [:meta {:charset "utf-8"}]
    [:meta {:name "viewport"
            :content "width=device-width, initial-scale=1.0"}]
    [:title "Tech blog"]
    [:link {:rel "stylesheet" :href "/styles/styles.css"}]]
   [:body
    [:div.logo "cjohansen.no"]
    [:div.body page]]))
As you can see, Hiccup markup is mostly a leaner version of HTML. One thing that makes Hiccup very cool is that is accepts elements like the ones in our example, nested lists of elements, and nils. This means that we can map over data structures inline in the Hiccup structure without having to worry about nested lists creating nested markup structures. Think of lists as the DocumentFragments of Hiccup. We can also inline if forms without else forms without worrying about dangling nils causing weird artefacts in the generated markup.
To use the new layout, we will add a page definition for our partial page, and apply the layout on it. To read files from the app's resources directory, require the clojure.java.io package:
(ns cjohansen-no.web
  (:require [clojure.java.io :as io]
            [hiccup.page :refer [html5]]
            [stasis.core :as stasis]))
Add the about page function:
(defn about-page [request]
  (layout-page (slurp (io/resource "partials/about.html"))))
Finally, add the new page to the page map created in get-pages:
(defn get-pages []
  (merge (stasis/slurp-directory "resources/public" #".*\.(html|css|js)$")
         {"/about/" about-page}))
Because we added a new dependency to project.clj, you now need to quit the server (Ctrl+C) and restart it. When you have, /about/ should present you with your fantastic new page-with-layout.

Serving all partials

Doing all this work for every new partial page seems a tad bit too laborious. We will generalize the code we just added by re-writing it to serve all partial pages under resources/partials in the same way.
Remember how (stasis/slurp-directory "resources/partials" #".*\.html$") will create a map with relative paths as keys and the contents of the corresponding files as values? This is almost what we want, except we now also want to wrap the content in the layout. To achieve this, we will loop through the map and wrap all the values in a function call that adds the layout. Like so:
(defn partial-pages [pages]
  (zipmap (keys pages)
          (map layout-page (vals pages))))
This function accepts as input the map produced by Stasis' slurp-directory and returns a map where the values have all been wrapped in a layout. The zipmap function takes two collections and returns a map. It builds each map entry by pulling an entry from the first collection to use as the key, and an entry from the second collection to use as the value. Now remove the about-page function, and update get-pages to look like this:
(defn get-pages []
  (merge (stasis/slurp-directory "resources/public" #".*\.(html|css|js)$")
         (partial-pages (stasis/slurp-directory "resources/partials" #".*\.html$"))))
This will almost work. You will find our about page from before at /about.html, instead of /about. The reason is that slurp-directory uses the relative file names as paths. We can fix this in one of two ways:
  1. Mapping the keys as well, to lose the extension. While this will certainly work, it will produce odd results for any file called index.html
  2. Rename resources/partials/about.html to resources/partials/about/index.html
We will opt for option 2 in this case. I will show option 1 when we deal with markdown files.

Path conflicts

Now that we have two page sources, and both of them create root-level URLs, there is a risk that we end up with conflicts. With our current use of merge, any page in resources/partials will silently shadow a page in resources/public with the same path. Update get-pages to the following to avoid the problem:
(defn get-pages []
  (stasis/merge-page-sources
   {:public (stasis/slurp-directory "resources/public" #".*\.(html|css|js)$")
    :partials (partial-pages (stasis/slurp-directory "resources/partials" #".*\.html$"))}))
The merge-page-sources function works pretty much like merge, except it will throw an exception if either source defines duplicate URLs. The map keys are useful to determine the source of the conflict. For instance, if you were to add public/about/index.html, you would get this error:
URL conflicts between :public and :partials: #{"/about/index.html"}
(That last bit is Clojure set notation by the way).

Writing in markdown

Partial pages are nice, but being able to write in markdown would be even better. Cegdown is a Clojure wrapper for Pegdown, a popular Java library for rendering markdown. Add it to your project.clj:
  :dependencies [[org.clojure/clojure "1.5.1"]
                 [stasis "1.0.0"]
                 [ring "1.2.1"]
                 [hiccup "1.0.5"]
                 [me.raynes/cegdown "0.1.1"]] ; Like so
Now, add it to the namespace form in src/cjohansen_no/web.clj. While we're at it, we will require the Clojure string library as well (we'll use it shortly):
(ns cjohansen-no.web
  (:require [clojure.java.io :as io]
            [clojure.string :as str]
            [hiccup.page :refer [html5]]
            [me.raynes.cegdown :as md]
            [stasis.core :as stasis]))
Now we will add a function to render every page in resources/md as markdown. It will be very similar to the partials we did before, but now with markdown rendering as well. Because we don't want ".md" as part of the URL for these pages, we will map the keys as well.
(defn markdown-pages [pages]
  (zipmap (map #(str/replace % #"\.md$" "") (keys pages))
          (map #(layout-page (md/to-html %)) (vals pages))))
The #( ) form is a function literal. Inside it, % refers to the first argument. In the above example, the following are identical:
#(str/replace % #"\.md$" "")
;; ...and:
(fn [path] (str/replace path #"\.md$" ""))
I wrote more on the anonymous function literal in a separate post.
The final step is to add the new page source to our map:
(defn get-pages []
  (stasis/merge-page-sources
   {:public
    (stasis/slurp-directory "resources/public" #".*\.(html|css|js)$")
    :partials
    (partial-pages (stasis/slurp-directory "resources/partials" #".*\.html$"))
    :markdown
    (markdown-pages (stasis/slurp-directory "resources/md" #"\.md$"))}))
Add the following to resources/md/my-first-post.md:
# My first post

It's pretty short for now.
Restart the server again (we added more new dependencies, remember?) When you have, /my-first-post should present you with your brief, but lovely blog post.

Post-processing: Syntax highlighting

Any self-respecting tech blog needs nice syntax highlighting for code blocks. When it comes to syntax highlighting, Pygments is the bee's knees. It supports just about any language you can think of, there's a bunch of color themes around and it is stable and resillient. It is also the library used to highlight code on GitHub. Clygments is a Clojure interface to it (which uses Jython; Pygments is a Python library).
We will add syntax highlighting as a post-processing step for HTML. This way, we can support syntax highlighting for full static pages in the public directory, partial pages and pages rendered from markdown. To do this, we will use another templating library for Clojure, enlive. Actually, enlive is more than just a templating library. As you will see, it can be used to transform documents in various interesting ways.

A code block

Let's add a fenced code block to our markdown file:
# My first post

It's pretty short for now. Here's our project.clj:

```clj
(defproject cjohansen-no "0.1.0-SNAPSHOT"
  :description "cjohansen.no source code"
  :url "http://cjohansen.no"
  :license {:name "BSD 2 Clause"
            :url "http://opensource.org/licenses/BSD-2-Clause"}
  :dependencies [[org.clojure/clojure "1.5.1"]
                 [stasis "1.0.0"]
                 [ring "1.2.1"]
                 [hiccup "1.0.5"]
                 [me.raynes/cegdown "0.1.1"]]
  :ring {:handler cjohansen-no.web/app}
  :profiles {:dev {:plugins [[lein-ring "0.8.10"]]}})
```
In order for this work, we need to inform cegdown that we want to enable the fenced code blocks extension. While we're at it, we'll enable a couple of other useful extensions as well:
(def pegdown-options ;; https://github.com/sirthias/pegdown
  [:autolinks :fenced-code-blocks :strikethrough])

(defn render-markdown-page [page]
  (layout-page (md/to-html page pegdown-options)))

(defn markdown-pages [pages]
  (zipmap (map #(str/replace % #"\.md$" "") (keys pages))
          (map render-markdown-page (vals pages))))
Reloading the blog post will show you how the fenced code blocks are rendered:
<pre><code class="clj">...</code></pre>
We will now use enlive to extract this piece of markup and replace it with the version highlighted by clygments. First off, add the new dependencies to project.clj (remember to restart the server!)
  :dependencies [[org.clojure/clojure "1.5.1"]
                 [stasis "1.0.0"]
                 [ring "1.2.1"]
                 [hiccup "1.0.5"]
                 [me.raynes/cegdown "0.1.1"]
                 [enlive "1.1.5"] ; New
                 [clygments "0.1.1"]] ; New
Add a new namespace (i.e., file) to the project. Copy the following code into src/cjohansen_no/highlight.clj:
(ns cjohansen-no.highlight
  (:require [clojure.java.io :as io]
            [clygments.core :as pygments]
            [net.cgrand.enlive-html :as enlive]))
Enlive has many tricks up its sleave. Perhaps the most interesting one is the somewhat confusingly named sniptest. It takes some HTML as a string, and selector/function pairs. It will then;
  1. Parse the HTML
  2. Find all nodes matching the selector
  3. Call the corresponding function once for every match
  4. Replace the node with the result of calling the function
  5. Return the transformed HTML as a string
(defn highlight-code-blocks [page]
  (enlive/sniptest page
            [:pre :code] highlight
            [:pre :code] #(assoc-in % [:attrs :class] "codehilite")))
This function will find every code element inside a pre element and pass it through the highlight function (to be shown). Then, it will add a class name to the same elements. In practice, you would probably do both in the highlight function, but this allows me to illustrate how you can perform multiple transformations in one go. The "codehilite" class name just happens to be class name used by the Pygments CSS themes available here (we will include this later). Add the second function:
(defn- highlight [node]
  (let [code (->> node :content (apply str))
        lang (->> node :attrs :class keyword)]
    (pygments/highlight code lang :html)))
The dash in defn- means that this function is private, and only referrable within the current namespace. The nodes that enlive operate on are maps like this:
{:tag :code
 :attrs {:class "clj"}
 :content [...]}
The content is a list of new nodes and/or strings.
->> is the thread-last macro. It takes any number of arguments, threads values from left to right; given (->> a (b 1 2) c), it will take the value a, pass it as the last argument to b (i.e. (b 1 2 a)), pass the return value of that expression as the last argument to c, and finally return the result of that. So the above line:
(->> node :content (apply str))
Is the same as this:
(apply str (:content node))
I wrote more on threading macros in a separate post.
Using a keyword as a function is one way to look up that key in a map. The apply call means to call str with the following list of arguments, as individual arguments, not one list (e.g. (fn a b c), not (fn [a b c])).
To preview the highlighting in the browser, we need to make some changes to the web namespace. Start by updating the namespace form to pull in our new dependency:
(ns cjohansen-no.web
  (:require [cjohansen-no.highlight :refer [highlight-code-blocks]] ; This one
            [clojure.java.io :as io]
            [clojure.string :as str]
            [hiccup.page :refer [html5]]
            [me.raynes.cegdown :as md]
            [stasis.core :as stasis]))
Syntax highlighting should apply to all pages. A good place to do it is between our old get-pages function and Stasis' rendering. We will do this by adding a prepare-pages function:
(defn prepare-pages [pages]
  (zipmap (keys pages)
          (map #(highlight-code-blocks %) (vals pages))))
Again, we use zipmap to produce a new map where the keys are untouched, but the values have been mapped. To make Stasis run through this, rename get-pages to get-raw-pages, and add a new get-pages:
(defn get-raw-pages []
  (stasis/merge-page-sources
   {:public
    (stasis/slurp-directory "resources/public" #".*\.(html|css|js)$")
    :partials
    (partial-pages (stasis/slurp-directory "resources/partials" #".*\.html$"))
    :markdown
    (markdown-pages (stasis/slurp-directory "resources/md" #"\.md$"))}))

(defn prepare-pages [pages]
  (zipmap (keys pages)
          (map #(fn [req] (highlight-code-blocks %)) (vals pages))))

(defn get-pages []
  (prepare-pages (get-raw-pages)))
Reloading the markdown page should show you that what we've done so far both kinda worked and kinda didn't.
We are getting highlighted code. However, Pygments includes some unwanted wrapping markup. As we're already inside a pre element, the result is not quite as desired. To fix this we will use enlive once more to massage the output from Pygments. The output includes a wrapper div and a pre, let's extract just the code:
(defn- extract-code
  [highlighted]
  (-> highlighted
      java.io.StringReader.
      enlive/html-resource
      (enlive/select [:pre])
      first
      :content))

(defn- highlight [node]
  (let [code (->> node :content (apply str))
        lang (->> node :attrs :class keyword)]
    (assoc node :content (-> code
                             (pygments/highlight lang :html)
                             extract-code))))
Enlive's select function selects elements from a document, but unlike sniptest, it does not accept a string. Instead, we must go through its html-resource function, which only accepts input streams. The end result is that we do a select on what Pygments gives us in order to get just the highlighted code. Refreshing the blog post shows that it works as expected.
The -> is the thread-first macro. It works like thread-last, except it threads values as the first argument to the next function. The above example could be written in either of these ways, but most people find the threading form to be the easiest on the eyes:
(-> highlighted
    java.io.StringReader.
    enlive/html-resource
    (enlive/select [:pre])
    first
    :content)

;; Same as:
(:content (first (enlive/select (enlive/html-resource
                                  (java.io.StringReader. highlighted))
                                [:pre])))
To add some styling, pick a CSS file from the suggested themes repo, and load it onto the page. Update the layout-page in web.clj to look like this:
(defn layout-page [request page]
  (html5
   [:head
    [:meta {:charset "utf-8"}]
    [:meta {:name "viewport"
            :content "width=device-width, initial-scale=1.0"}]
    [:title "Tech blog"]
    [:link {:rel "stylesheet" :href "/pygments-css/autumn.css"}]]
   [:body
    [:div.logo "cjohansen.no"]
    [:div.body page]]))

Lazy pages

As we're adding more features to our site, it is becoming apparent that processing all the pages to completion on every request isn't ideal. Fixing this is quite easy with Stasis, because we can give Stasis functions instead of strings, and then Stasis will call the function to build a particular page only when it needs to render that specific page.
To make our pages lazy, update prepare-pages to replace the values with functions instead of strings of highlighted HTML. The function should take one argument, the request map.
(defn prepare-pages [pages]
  (zipmap (keys pages)
          (map #(fn [req] (highlight-code-blocks %)) (vals pages))))
By having the function literal return a new function that takes one argument, we have significantly improved performance for our development server.

Asset optimization

Now that we have a blog with syntax highlighting, we need to start thinking about delivery. Fast webpages beat slow ones on all sorts of metrics. One way to make our site faster is by employing various frontend asset optimization techniques. For this purpose, there is (among others) Optimus. We will use it to:
  • Concatenate CSS and JavaScript files
  • Minify CSS and JavaScript files
  • Serve CSS and JavaScript from cache-friendly URLs
A "cache friendly" URL is one that is unique every time the contents of the URL changes. This way we can serve assets with aggressive cache headers, and users will only need to download them once. The next time we deploy, if the assets have changed, they will have a new URL. Optimus facilitates this by providing some functions to help us link to assets.
We will start by adding Optimus as a dependency in project.clj. Remember to restart the server after doing this.
(defproject cjohansen-no "0.1.0-SNAPSHOT"
  :description "cjohansen.no source code"
  :url "http://cjohansen.no"
  :license {:name "BSD 2 Clause"
            :url "http://opensource.org/licenses/BSD-2-Clause"}
  :dependencies [[org.clojure/clojure "1.5.1"]
                 [stasis "1.0.0"]
                 [ring "1.2.1"]
                 [hiccup "1.0.5"]
                 [me.raynes/cegdown "0.1.1"]
                 [enlive "1.1.5"]
                 [clygments "0.1.1"]
                 [optimus "0.14.2"]] ; New
  :ring {:handler cjohansen-no.web/app}
  :profiles {:dev {:plugins [[lein-ring "0.8.10"]]}})
Now update the web.clj namespace form to require some functions from Optimus:
(ns cjohansen-no.web
  (:require [optimus.assets :as assets]                     ; New
            [optimus.optimizations :as optimizations]       ; New
            [optimus.prime :as optimus]                     ; New
            [optimus.strategies :refer [serve-live-assets]] ; New
            [cjohansen-no.highlight :refer [highlight-code-blocks]]
            [clojure.java.io :as io]
            [clojure.string :as str]
            [hiccup.page :refer [html5]]
            [me.raynes.cegdown :as md]
            [stasis.core :as stasis]))
Instead of having Stasis serve the files in public, we will hand them to Optimus as assets. We will define a separate function for these assets, as it makes for a natural place to add further assets and/or bundles of assets later:
(defn get-assets []
  (assets/load-assets "public" [#".*"]))
If your CSS files use @import, Optimus will (by default) take care to inline the import, so there is no need to define bundles at this point. Refer to the Optimus readme for more details.
To make our app use the new assets, we will change the app function:
(def app
  (optimus/wrap (stasis/serve-pages get-pages)
                get-assets
                optimizations/all
                serve-live-assets))
The call to stasis/serve-pages returns a function (a Ring app, remember?) optimus/wrap returns another function with the same signature that wraps the original one. We pass it the function to get all our assets, optimization rules (a function) and a strategy for serving the assets (also a function). optimizations/all is a grab bag of every trick Optimus knows:
  • Minify JavaScript
  • Minify CSS
  • Inline CSS imports
  • Concatenate bundles
  • Add cache-bust expires headers (replace URL references with generated unique ones)
  • Add last-modified headers
You are free to pick and choose from this list if you want, but for most cases, optimizations/all is what you want.
Lastly, we employed Optimus' serve-live-assets strategy, which means that Optimus will read assets from disk on every request. This is useful in development mode, but in a production setting, you would typically use one that's less resource intensive, like serve-frozen-assets.
Create a CSS file and make sure it gets included from the page layout.
@import url(../pygments-css/autumn.css);

body {
    font: 16px Helvetica, arial, freesans, clean, sans-serif;
    line-height: 1.5;
    margin: 0 10px;
}
Refreshing the blog in the browser should display the same page as before. However, if you hit the CSS file directly, you will find that Optimus has done what it can to optimize serving it.

Rewriting links

There is one final thing to take care of. In production, we can configure our web server to serve assets with a far future expires header. But in order for that to be safe, we need distinct URLs for every change to the file. Let's add another require to the web namespace form:
(ns cjohansen-no.web
  (:require [optimus.assets :as assets]
            [optimus.link :as link] ; New
            [optimus.optimizations :as optimizations]
            [optimus.prime :as optimus]
            [optimus.strategies :refer [serve-live-assets]]
            [cjohansen-no.highlight :refer [highlight-code-blocks]]
            [clojure.java.io :as io]
            [clojure.string :as str]
            [hiccup.page :refer [html5]]
            [me.raynes.cegdown :as md]
            [stasis.core :as stasis]))
With this in place, we can use Optimus to generate the link to the CSS file. However, to do that, it needs access to the request map, so we need to change a few things. We will start with the layout-page function:
(defn layout-page [request page]
  (html5
   [:head
    [:meta {:charset "utf-8"}]
    [:meta {:name "viewport"
            :content "width=device-width, initial-scale=1.0"}]
    [:title "Tech blog"]
    [:link {:rel "stylesheet" :href (link/file-path request "/styles/main.css")}]]
   [:body
    [:div.logo "cjohansen.no"]
    [:div.body page]]))
Both the partial-pages and markdown-pages need to pass the request to layout-page. If we change them to return functions, Stasis will call those functions with the request.
(defn partial-pages [pages]
  (zipmap (keys pages)
          (map #(fn [req] (layout-page req %)) (vals pages))))
Remember that #( ) is a function literal, so the mapping function here is a function that returns another function (which takes a request map as its only argument). The markdown generation is similar, but includes the additional step of running the content through cegdown:
(defn markdown-pages [pages]
  (zipmap (map #(str/replace % #"\.md$" "") (keys pages))
          (map #(fn [req] (layout-page req (md/to-html % pegdown-options)))
               (vals pages))))
Previously these maps contained strings, so we need to update their use now that they're functions. We start with a new function:
(defn prepare-page [page req]
  (-> (if (string? page) page (page req))
      highlight-code-blocks))
This function takes a page and a request. Because every page will go through this function, some will be strings, and some will be functions. If the page is a string, we leave it untouched, and if it's a function, we call it with the request map and pipe the result through a series of post-processing steps. There's currently only one step, but the threading macro has set us up for easily adding more steps later. The final piece of the puzzle is to update the prepare-pages function:
(defn prepare-pages [pages]
  (zipmap (keys pages)
          (map #(partial prepare-page %) (vals pages))))
Again, we use the function literal #( ). We also use partial. This returns a new function that knows the first argument to pass to prepare-page. When you call this new function with one argument (a request), the prepare-page function will be called with a page and a request. Update the page in the browser, view source and note that Optimus has now given our CSS file a nice and unique URL.

Export to disk

So far we've only surfed the server version, but the whole point of this exercise was to create something that can work as a static site. To dump the file to disk, start by adding a custom Leiningen build alias in project.clj:
(defproject cjohansen-no "0.1.0-SNAPSHOT"
  :description "cjohansen.no source code"
  :url "http://cjohansen.no"
  :license {:name "BSD 2 Clause"
            :url "http://opensource.org/licenses/BSD-2-Clause"}
  :dependencies [[org.clojure/clojure "1.5.1"]
                 [stasis "1.0.0"]
                 [ring "1.2.1"]
                 [hiccup "1.0.5"]
                 [me.raynes/cegdown "0.1.1"]
                 [enlive "1.1.5"]
                 [clygments "0.1.1"]
                 [optimus "0.14.2"]]
  :ring {:handler cjohansen-no.web/app}
  :aliases {"build-site" ["run" "-m" "cjohansen-no.web/export"]} ; New
  :profiles {:dev {:plugins [[lein-ring "0.8.10"]]}})
This configures `lein build-site` as a command that will invoke the export function in the cjohansen-no.web namespace. Stasis gives us what we need to build this function:
(def export-dir "dist")

(defn export []
  (stasis/empty-directory! export-dir)
  (stasis/export-pages (get-pages) export-dir))
While this won't technically fail, it also won't be the whole picture. Had we not been using Optimus, this would be OK. Since we are using Optimus, we want to make sure the export is optimized as well. The fix is simple; tell Optimus to dump assets for us, and add an entry to Stasis' request map extensions so that Optimus finds the assets. First update the namespace form to require the Optimus export library:
(ns cjohansen-no.web
  (:require [optimus.assets :as assets]
            [optimus.export]            ; New
            [optimus.link :as link]
            [optimus.optimizations :as optimizations]
            [optimus.prime :as optimus]
            [optimus.strategies :refer [serve-live-assets]]
            [cjohansen-no.highlight :refer [highlight-code-blocks]]
            [clojure.java.io :as io]
            [clojure.string :as str]
            [hiccup.page :refer [html5]]
            [me.raynes.cegdown :as md]
            [stasis.core :as stasis]))
Then update the export function:
(defn export []
  (let [assets (optimizations/all (get-assets) {})]
    (stasis/empty-directory! export-dir)
    (optimus.export/save-assets assets export-dir)
    (stasis/export-pages (get-pages) export-dir {:optimus-assets assets})))
Now, on the command line, run lein build-site. After a short while you will find your entire site ready to ship in the dist directory. This can be directly rsynced to your server.

Testing and verification

Building sites like we've done in this post opens for various interesting ways of programatically performing tests and health checks. I will show you two simple, yet immensely useful tests we can add to a site of this kind. You can of course also add unit tests for individual functions, and doing so in a system composed of mostly pure functions is very straight-forward, yet outside the scope of this post.

Testing for 200 OK

One nice test to put in a site like this is an integration test that checks that every page renders without errors. We will use Midje for our tests, so let's update project.clj:
(defproject cjohansen-no "0.1.0-SNAPSHOT"
  :description "cjohansen.no source code"
  :url "http://cjohansen.no"
  :license {:name "BSD 2 Clause"
            :url "http://opensource.org/licenses/BSD-2-Clause"}
  :dependencies [[org.clojure/clojure "1.5.1"]
                 [stasis "1.0.0"]
                 [ring "1.2.1"]
                 [hiccup "1.0.5"]
                 [me.raynes/cegdown "0.1.1"]
                 [enlive "1.1.5"]
                 [clygments "0.1.1"]
                 [optimus "0.14.2"]]
  :ring {:handler cjohansen-no.web/app}
  :aliases {"build-site" ["run" "-m" "cjohansen-no.web/export"]}
  :profiles {:dev {:plugins [[lein-ring "0.8.10"]]}
             :test {:dependencies [[midje "1.6.0"]]    ; New
                    :plugins [[lein-midje "3.1.3"]]}}) ; New
We've added a test profile that includes the midje dependencies. Add the following to test/cjohansen_no/web_test.clj:
(ns cjohansen-no.web-test
  (:require [cjohansen-no.web :refer :all]
            [midje.sweet :refer :all]))

(fact
 "All pages respond with 200 OK"

 (doseq [url (keys (get-pages))]
   (let [status (:status (app {:uri url}))]
     [url status] => [url 200])))
We simply call our get-pages function, loop the resulting map, and call each page function with a request map consisting only of a URL. The comparison is made with a vector of the URL and the status. The reason for this is that the URL will be included in the error message if this fails. This way we can know which pages fail. To run the tests:
lein with-profile test midje
Doing this will inform us that the generated core_test.clj fails. Just delete it. Other than that, the test confirms that all is well with our site. To keep the tests running while working on the site, run autotest:
lein with-profile test midje :autotest

Building a link checker with enlive

Another useful test to have in place is a link-checker. We will make one that at least verifies that the internal links between pages in our app are correct, and that they don't cause any unnecessary redirects (e.g. from /about to /about/).
Enlive is very useful for these things. We will use the select function to find all links, and then make sure that the href attribute points to an existing URL if it is a path (not a full URL, which is treated as an external link). First up is the link-valid? function, which checks if a single link is valid given a map of pages:
(defn link-valid? [pages link]
  (let [href (get-in link [:attrs :href])]
    (or
     (not (.startsWith href "/"))
     (contains? pages href)
     (contains? pages (str href "index.html")))))
The link is considered valid if the href attribute either points to a URL that isn't a path within our app (relative paths are assumed not used) or if it points to one of the pages in the map. We're lenient enough to allow links to /about/ when we have /about/index.html. Since we will use enlive to select all the links, update the test namespace form to this:
(ns cjohansen-no.web-test
  (:require [cjohansen-no.web :refer :all]
            [midje.sweet :refer :all]
            [net.cgrand.enlive-html :as enlive]))
Then add the test itself:
(fact
 "All links are valid"

 (let [pages (get-pages)]
   (doseq [url (keys (get-pages))
           link (-> (:body (app {:uri url}))
                      java.io.StringReader.
                      enlive/html-resource
                      (enlive/select [:a]))]
     (let [href (get-in link [:attrs :href])]
       [url href (link-valid? pages link)] => [url href true]))))
Again, we loop all the pages and get them. For each page, we select all links, and expect all of them to pass the link checker. Again we make a slightly strange comparison in the interest of having more than true/false in the output if one of these fail. If you add an invalid link to the markdown file now, running the tests will produce this:
FAIL "All links are valid" at (web_test.clj:21)
    Expected: ["/my-first-post" "/about/" true]
      Actual: ["/my-first-post" "/about/" false]
18 lines of code to verify all links on the site. Pretty nifty! Rather than having numerous tests that load all the pages, it would probably be a good way to change the structure of the tests such that we only load each page once, and instead register various test functions we want to run for each page. This is left as an exercise for the reader.

Summary

I hope this post has shown you the power and flexibility of Stasis and all the other tools. Perhaps it has even convinced you further of the value of Clojure. I really do dislike number-of-lines-of-code jerkoffs, but it is worth mentioning that we were able to build a reasonably feature-complete technical blog in roughly 100 lines of code using a simple, yet powerful "no batteries included" library like Stasis (which itself clocks in at just over 100 lines of code). I hope you will consider Clojure and Stasis for your next semi-static web project.
Big thanks to Magnar Sveen for proof-reading and correcting this post.

Discuss

from http://webcache.googleusercontent.com/search?q=cache:xOk4FxU2v2EJ:cjohansen.no/building-static-sites-in-clojure-with-stasis+&cd=1&hl=zh-CN&ct=clnk&gl=uk

http://cjohansen.no/building-static-sites-in-clojure-with-stasis


https://archive.today-保存网页快照的服务

https://archive.today/
第一次见到.today这个TLD后缀