ruby实现的文本博客引擎构建(一)

本博客使用的是一个叫toto的文本博客引擎,
toto采用文本而不是数据库来存储数据,这样就省去数据库的开销,
完全使用静态文件渲染的方法来构建网站,
很适合挂在heroku中的免费项目下.

本文是我研究toto代码并修改、实现的过程,暂时称之为txtblog.

1.先运行起来!

要使自己的网站运行起来,即可以通过浏览器以http协议形式访问,我们需要Rack模块.
rack是一个标准的Ruby网络服务运行接口(a modular Ruby webserver interface).

我们就用它来构建网站的第一步:

要使rack运行起来,只需要一个包含call方法的类,在其中引入rack的requset和response就可以了.
如下为第一步:

 1    #/home/myhome/v1/txtblog.rb
 2    class Server
 3        def call env
 4            @request  = Rack::Request.new env
 5            @response = Rack::Response.new
 6            body = ["<h1>hello world</h1>","<h2>This is a simple website.</h2>"]
 7            @response.body= body
 8            @response['Content-Type']   = Rack::Mime.mime_type(".html")
 9            @response['Cache-Control'] = "no-cache, must-revalidate"
10            @response.status = 200
11            @response.finish
12        end
13    end

然后定义config.ru内容

1    #/home/myhome/v1/config.ru  
2    require './txtblog'
3    use Rack::ShowExceptions
4    myserver = Server.new
5    run myserver

接着在当前目录(config.ru所在目录)下运行 thin start -p 3001,
在浏览器中打开 http://localhost:3001 即可看到返回的内容.

以上即为一个简单的Web服务器,接下来我们希望能够读取出文件的内容来替换txtblog.rb中定义的body.

2.读取网站模板

网站模板也可以说成是网站的"屏幕架子",我们的博客内容就是屏幕里的画面.
此处使用的是可以嵌入ruby程序的html文件(rhtml),使用的ruby模块叫ERB(embedded ruby).

为了便于控制我们将第一步中txtblog.rb中的Server类放在叫Txtblog的module中,
然后再在Txtblog中定义一个Site类来读取网站模板,并在Server类中引用它.

 1    module Txtblog
 2        class Site
 3            def index
 4                title = "Index"
 5                ERB.new(File.read("templates/layout.rhtml")).result(binding)
 6            end
 7        end
 8        class Server
 9        def initialize
10            @site = Txtblog::Site.new
11        end
12        def call env
13            @request  = Rack::Request.new env
14            @response = Rack::Response.new
15            #body = ["<h1>hello world</h1>","<h2>This is a simple website.</h2>"]
16            body = []
17            body << @site.index
18            @response.body= body
19            @response['Content-Type']   = Rack::Mime.mime_type(".html")
20            @response['Cache-Control'] = "no-cache, must-revalidate"
21            @response.status = 200
22            @response.finish
23        end
24    end
25    end

这样,我们就成功地读取出来Erb文件中的内容并向其中内嵌的title变量赋值.
运行thin start -p 3001即可看到读取出来的结果.

这里需要补充的是如果所读取的erb文件中包含静态的如css、javascript、图片,
需要在config.ru中引用rack的Static来设置网站的根目录.
下边的这段代码表示将public文件夹设置为根目录,其下的css,js,images文件夹为可访问目录.

1    use Rack::Static, :urls => ['/css', '/js', '/images','/favicon.ico'], :root => 'public'

例如:如果要引用css文件夹内的app.css,
只需指明路径 /css/app.css即可,而不是/public/css/app.css.

3.加载markdown

前边提到我们的博客是"文本"形式的,那么是不是写成包含html标签的文档呢?

肯定不能那么繁杂,此处我们介绍一中类似html标签,却是更简洁的文本格式:markdown.
ruby中使用rdiscount这样一个gem可以实现将markdown转换为html标记.
关于markdown的详细规则可以参考其官网教程.

我们重新修改代码如下:

 1    require 'rdiscount'
 2
 3    module Txtblog
 4        class Server
 5            def initialize
 6                @site = Txtblog::Site.new
 7            end
 8            def call env
 9                @request  = Rack::Request.new env
10                @response = Rack::Response.new
11
12                #body = ["<h1>hello world</h1>","<h2>This is a simple website.</h2>"]
13                body = []
14                body << @site.index(&Proc.new{ @site.markdown })
15                @response.body= body
16                @response['Content-Type']   = Rack::Mime.mime_type(".html")
17                @response['Cache-Control'] = "no-cache, must-revalidate"
18                @response.status = 200
19                @response.finish
20            end
21        end
22
23        class Site
24            def index &blk
25                title = "Index--"
26                ERB.new(File.read("templates/layout.rhtml")).result(binding)
27            end
28            def markdown
29            Markdown.new(File.read("articles/test.md")).to_html
30          end
31        end
32    end

需要声明的一点是在layout.rhtml中要加入<%= yield %>这样一句嵌入语句.
它表示将传给方法的代码块放入该位置来迭代.
我们通过Proc.new将markdown渲染出来的文本带入到index读取的模板中,
从而实现从"屏幕(框架)"中显示"影像(博客)"来.

总结

该部分只是简单地将文本引擎的流程描述出来,更细致的东西如路由响应等尚未实现.
并且上述代码着实很"丑陋",在下一步的描述中会逐步重构.
但是作为一个简单的example,我想我应该展示了所有的东西.

Powered by Engin & toto

comments powered by Disqus