阿里云「飞天发布时刻」2024来啦!新产品、新特性、新能力、新方案,等你来探~ 了解详情
写点什么

用 Sinatra 编写博客应用

  • 2011-01-21
  • 本文字数:4537 字

    阅读完需:约 15 分钟

Sinatra 是 Blake Mizerany 在 2007 年 9 月开发的 Ruby 语言的 Web 框架。它最突出的特点就是轻量、快速。更难能可贵的是,Sinatra 的源代码只有一千多行。

在第一次接触到 Sinatra 的时候,我便被它深深地吸引住了。随后,我在 09 年 3 月的 Shanghai on Rails 活动向大家介绍了这个框架。10 年 8 月份我有幸可以在 RubyKaigi 这样的全球级 Ruby 社区会议上作为演讲者和听众交流 Sinatra。本文则是对 10 年 10 月份在上海 Linux 用户组介绍 Sinatra 的讲座的一些整理和总结。希望读者能够通过本例子能体会到 Sinatra 的精妙之处。

最新版本: 1.1

截止到本文成文为止,Sinatra 最新的版本是 10 年 10 月 24 日发布的 1.1 版本。很幸运的是,我对于 README 的翻译正好在发布的前一天被合并进入了主分支。于是在 1.1 的正式版本中,中文的读者可以直接阅读到中文的 README,从而更好的了解 Sinatra 的用法。官网上也有此文档的链接, http://www.sinatrarb.com/intro-zh.html 。本文的代码全部以 1.1 版本为准。

Sinatra 的基本结构

让我们从 Sinatra 最常见的 Hello world 程序开始:

复制代码
get '/' { "Hello, world!" }

这段简单的 Hello world 程序包含了 Sinatra 程序的三个基本组成部分:

  • 路由(route):

    ‘/’ 就是路由。路由可以是单一的路径,或者带有参数的路径(比如 /:name),甚至是正则表达式。对于 Sinatra 不知道的路由,Sinatra 会返回 404 错误(作为 App 运行的时候),或者传递给下面的中间件(作为中间件运行的时候)。

  • 方法(method):

    get是方法。在 Sinatra 中,HTTP 的四个方法GET/POST/PUT/DELETE都有相应的方法get/post/put/delete

  • 处理器(handler):

    处理器就是最后的代码块,处理器的返回值就是 Sinatra 返回给客户端(主要是浏览器)的内容。返回值主要以字符串为主,也可以是包含状态码,消息头,消息体的数组。

渲染模版

Sinatra 支持的模版类型也在逐渐增加中。Haml 是笔者常用的格式,因为它使用了 CSS 选择符构造 HTML 标签,从而节省编写时间。另一种常见的格式是 Ruby 自带的 ERB,本例子将使用 Haml 作为博客的模版。

渲染模版在 Sinatra 中是很容易的事:

复制代码
get '/' do
haml :index
end

在这里haml :index,就表示使用 Haml 渲染'views/index.haml'这个模版。

传递参数也是很容易的事,可以使用实例变量:

复制代码
# in app.rb
get '/' do
@now = Time.now
haml :index
end
# in views/index.haml
Hello, now is #{@now}

或者用 locals 传递参数(如例子中的哈希):

复制代码
# in app.rb
get '/' do
now = Time.now
haml :index, :locals => { :now => now }
end
# in views/index.haml
Hello, now is #{now}

熟悉了路由和模版,就可以开始构建 Web 应用程序了,Sinatra 也提供了一些简单的辅助方法,比如过滤器、helpersconfigurehaltpass等等,这些就不再这里一一叙述了,更多的内容请仔细参考官方文档。

开始博客应用

文件格式

本博客应用将使用 dorothy 格式的文件存储,不会使用数据库。

例子如下:

复制代码
# 文件名: 2010-10-10-a-lucky-day.txt
title: "A Lucky Day"
date: 2010-10-10
author: " 吴江 "
# 今天是我的幸运日
早上在地铁门将要关上的那一刻,我冲进了车厢,于是约会没有迟到...
中午提前了一点去港丽,居然只排了 42 分钟...
晚上又赶上了末班车...
到家数了数,钱包里面正好有 42 块钱...

该文件的结构是:以第一个连续换行符("\n\n")为界线,前一半是 YAML 格式的配置信息,后一半则是 markdown 格式的文本。 YAML 格式是一种表示数据的标记语言。这里只使用到它的键值对结构。 markdown 则是很方便的用纯文本编写 HTML 的格式。比如"# header1"会生成"<h1>header1</h1>""*emphasis*"会生成"<em>emphasis</em>"等等。

安装环境

本博客应用使用 Ruby 1.8.7 版本。安装好后,首先安装 Bundler(gem install bundler),然后编写 Gemfile(见下),运行bundle install即可一次性安装好所需的 gems。

复制代码
# Gemfile
source "http://rubygems.org"
gem 'haml' # Haml 模版
gem 'rdiscount' # 渲染 Markdown
gem 'sinatra' # Sinatra
gem 'thin' # 应用服务器
gem 'shotgun' # 重启服务器
group :test do
gem 'rspec' # 单元测试
gem 'nokogiri' # 解析 HTML 输出
end

测试驱动开发

使用测试驱动开发并非为了赶时髦,只是为了能够帮助我们写出更好的代码。

在本例子中,我们的测试需要能够达到以下目标:

  1. 访问"/"的时候能够正确返回文章列表(虽然只有一篇文章)
  2. 访问"/:year/:month/:date/:title"的时候能够正确地展示文章内容

正式编写

在本例子中,将只接受两个路由请求,'/''/:year/:month/:date/:title'

首先编写如下的测试:

复制代码
# in app_spec.rb
describe 'blog' do
before do
@req = MockRequest.new(Sinatra::Application)
end
it "should show index correctly" do
resp = @req.get '/'
resp.status.should == 200
end
end

运行rspec app_spec.rb可以看到失败结果。先编写简单的代码让测试通过。

复制代码
# in app.rb
get '/' do
""
end

然后继续增加测试,我们想让返回的页面中有链接到/2010/10/10/a-lucky-day这个日志的链接

复制代码
# in app_spec.rb
...
it "should show index correctly" do
resp = @req.get '/'
resp.status.should == 200
doc = Nokogiri.new(resp)
(doc/'a[href="/2010/10/10/a-lucky-day"]').text.should == "A Lucky Day"
end

为了通过这个测试则要写一些长一点的代码,为了省略篇幅,Article类的代码在这里忽略:

复制代码
# in app.rb
get '/' do
@articles = []
Dir.glob("articles/*.txt").each do |article_file|
@articles << Article.new(article_file)
end
haml :index
end

在上文的代码中,首先读取了 articles 目录下的所有 txt 后缀的文件,就是全部的日志。 并把这些日志装到@articles这个数组类型的实例变量。

在视图中,则简单的把日期和日志名称罗列出来。

复制代码
# in views/index.haml
...
- @articles.each do |article|
%header
%h2
= article.date.strftime("%Y 年 %m 月 %d 日 ")
%a{ :href => article.path }= article.title

接下来使用同样的方式来编写显示日志具体内容的代码:

复制代码
it "should show article correctly" do
resp = @req.get '/2010/10/10/a-lucky-day'
resp.status.should == 200
doc = Nokogiri(resp.body)
(doc/'title').text.should == "A Lucky Day"
(doc/'article h1').text.should == " 今天是我的幸运日 "
resp.body.should match " 钱包里面正好有 42 块钱 "
end

实现所用的代码相对会少一些:

复制代码
# in app.rb
get '/:year/:month/:day/:title' do |year, month, day, title|
article_file = "articles/#{year}-#{month}-#{day}-#{title}.txt"
@article = Article.new(article_file)
haml :show
end
# in views/show.haml
!!!
%html
%head
%title= @article.title
%body
%header
%h1
= @article.title
%article= @article.body

测试通过以后,也可以使用shotgun app.rb -s thin开启服务器, 访问 http://localhost:9393 就可以看到在浏览器中的效果。

部署

Heroku 是目前为止最好用的 Ruby 应用部署服务之一。在 Heroku 的帮助下,我们可以快速地把这个应用发布给全世界使用。

首先编写config.ru

复制代码
# in config.ru
run Sinatra::Application

然后运行如下代码:

复制代码
# git 初始化
git init .
git commit -a -m "Initial Commit"
# heroku 部署
heroku create
git push heroku master

当看到"Launching … done"的字样的时候,就说明我们的程序部署成功了,赶快点击下面的链接看看结果吧!

评论

Disqus 是目前我知道的最好用的评论管理系统。更要命的是,它能够很简单的把一个评论系统加到我们的博客中:

复制代码
<section class="comments">
<script type="text/javascript" src="http://disqus.com/forums/#{username}/embed.js">
</section>

只要把上面这段 html 代码加入到我们的系统中,一个完善的评论系统就出现在用户的眼前。本地调试的时候则要额外加上一句:

复制代码
<script type="text/javascript">var disqus_developer = 1;</script>

借助了 Disqus,我们的评论系统就不会逊色于任何的博客应用。

思考

如果读者能够在整个过程中感受到快乐或者惊奇,那么我编写本文章的目的就算达到了。 详细的代码请参考本文的项目地址: https://github.com/nouse/text-blog

以下则为笔者在制作这个应用过程之中的一些思考。

5 年前,Rails 的创造者 David Heinemeier Hansson 向全世界介绍了 15 分钟编写 blog 应用(优酷视频链接)。在5 年后,我们又用Sinatra 重复造轮子,如果读者对比两者的差别, 就能深刻感觉到这5 年里Ruby 世界的一些变化。

基本工具(RVM 和Bundler)

这5 年间,Ruby 基本工具有了很大的发展。这其中最大的亮点就是 RVM(Ruby Version Manager)。 除了如它的名字所述,可以帮助开发人员安装不同版本的 Ruby 以外。它的 gemset 功能也非常 好用。不同的 gemset 之间是一个个独立的环境,从而避免同一个 gem 的不同版本之间的干扰。

如果在项目目录下添加.rvmrc(rvm use version@gemset),就可以让项目处于一个独立的环境之中。 再编写好 Gemfile,将项目中需要的 Ruby 库全部交给 Bundler 管理, 就不会出现部署的时候缺乏相应的库导致失败的情况了。

方便的部署

Git 的普及和 Heroku 的崛起,大大简化了部署的过程。如果 5 年前有 Heroku 的话, DHH 的博客应用可以有更大的反响。“编写完成”–>“git push”–>“上线!”。 一个博客应用就一瞬间仿佛活了一样,从一个本地的演示项目变成了一个真正的线上应用。

Disqus 等第三方应用的兴起

5 年前,Web 2.0 刚刚兴起,只要编写一个使用 Ajax 增强交互功能的应用, 就可以吸引用户的眼球。但是随着 Web 2.0 的概念深入人心,做一个 blog 显然不再能吸引用户的眼球了。

如果 Disqus 这样的第三方应用能够逐渐增多,那么我们就能够把更多的时间放在我们真正想实现的功能上。 就像这里,我们只要把博客的内容展示做好就够了,其他的则交给成熟的服务来处理。 Rails 的成功就在于简化了开发 Web 2.0 应用的时间。借用一下 jQuery 的口号“write less, do more”, “写的更少,做的更多”是软件开发永远的主题。

Sinatra 和 Rails 的关系

DHH 在推出 Rails 的时候,让深陷于 Java 世界的开发人员看到了希望,Rails 也借助 Web 2.0 的热潮迅速走红。 其实,笔者所做的演示的功能模仿的是一个 Rack 应用程序, toto 。 所以读者们也不必迷信,用 Sinatra 经过 15 分钟能做出更好的博客应用,就说明 Sinatra 会取代 Rails。

当前最流行的方式是融合,比如 gemcutter.org,也就是现在的 rubygems.org。 他们整个站点使用的是 Rails 3,而客户下载 gem 的请求则是被 Sinatra 处理。 这样就可以保证网站在升级的时候不会影响下载 gem 的请求,而且 Sinatra 处理请求的速度也优于 Rails 3, 用来处理每天超过访问网站数倍的下载请求也十分合适。

不管怎样,只有更多的了解一个框架的优缺点,才能在真正使用的时候做出正确的选择。而 Sinatra 的源代码只有一千行,要了解它并做出选择,相信不是件难事。


关于作者:吴江,Ruby 和 Javascript 程序员。从 09 年开始,在国内社区中积极宣传和推广 Sinatra。10 年 8 月底,以演讲者的身份参加了在日本举行的 RubKaigi。现在上海一家 Ruby 行业的咨询公司工作。

2011-01-21 00:0010280

评论

发布
暂无评论
发现更多内容

4KMILES加入艾盛集团,携手打造中国品牌在全球网络的增长机遇

Geek_2d6073

昇思MindSpore 1.8:丰富的算法集与套件,实现高性能训练,降低部署门槛

科技热闻

五大数据安全保障措施看这里!

行云管家

信息安全 数据安全 企业安全 数据库审计

终究还是错付了!这2种Python字符串格式化的写法已经被淘汰了,你是不是还在用?

程序员晚枫

Python 字符串 格式化

C#/VB.NET: 改变Word中的字体颜色

Geek_249eec

C# word VB.NET 改变字体颜色

Linux下玩转nginx系列(八)---如何使用upsync模块实现动态负载均衡

anyRTC开发者

nginx Linux 负载均衡 音视频 服务器

MobPush丨iOS端SDK API

MobTech袤博科技

ios API MobTech袤博科技 mobpush

开源一夏|OpenHarmony之如何实现震动

坚果

开源 OpenHarmony 8月月更

架构实战营模块九作业

Geek_Q

如何给玩偶建模并让它跳个舞?

HMS Core

开源一夏 | 实战Node.js原理对于阻塞和EventEmitter及其继承的运用心得

恒山其若陋兮

开源 8月月更

2022BATJ1000道Java面试题解析,已有372人上岸(必看攻略)

程序知音

Java 程序员 java面试 后端技术 Java八股文

兆骑科创创服平台,招商引资,招才引智,投融资对接

兆骑科创凤阁

【Django | allauth】重写allauth重置密码方法

计算机魔术师

8月月更

业务数据迁移上云的一些技术思考

京东科技开发者

MySQL 迁移 云数据库Redis

鄢贵海:DPU发展中的四个关键问题

硬科技星球

精益+敏捷,两大管理思路让研发效能「飞」起来

万事ONES

兆骑科创创新创业服务平台,海内外高层次人才引进,活动赛事

兆骑科创凤阁

Vue3知识点梳理(一)

青柚1943

Vue3

RT-Thread记录(九、RT-Thread 中断处理与阶段小结)

矜辰所致

RT-Thread 8月月更

微服务性能分析|Pyroscope 在 Rainbond 上的实践分享

北京好雨科技有限公司

Kubernetes 微服务 云原生

出海浪潮下,Edgio引领CDN安全新风向!

科技热闻

4KMILES加入艾盛集团,加速中国品牌跨境业务的全域全效增长

Geek_2d6073

5K字详解HttpClient 在vivo内销浏览器的高并发实践优化

了不起的程序猿

Java 后端 HTTP 并发 java程序员

创新能力加速产业发展,SphereEx 荣获“中关村银行杯”『大数据与云计算』领域 TOP1

SphereEx

数据库 开源 架构 SphereEx Apache ShardingSphere

Android进阶(十六)子线程调用Toast报Can‘t create handler inside thread that has not called Looper.prepare() 错误

No Silver Bullet

android 8月月更 toast

Go 事,Gopher 要学的数字类型,变量,常量,运算符 ,第2篇

梦想橡皮擦

Python 爬虫 8月月更

语音直播系统源码——解决应用瘦身问题

开源直播系统源码

软件开发 语聊房 语音直播系统 语音直播系统连麦

大咖说·对话开源|企业如何用好开源数据库

大咖说

开源 企业数据库

【Django | allauth】登录_注册_邮箱验证_密码邮箱重置

计算机魔术师

8月月更

【Django | allauth】useprofile 用户模型扩展

计算机魔术师

8月月更

用Sinatra编写博客应用_Ruby_吴江_InfoQ精选文章