写点什么

用 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:0010588

评论

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

synchronized 和 ReentrantLock 的区别

zarmnosaj

10月月更

数字化转型必读:从信息化到数字化的本质是什么

雨果

数字化转型 数字化 数字化业务转型 信息化本质

如何给TiDB集群的prometheus更换端口

TiDB 社区干货传送门

实践案例 集群管理 安装 & 部署 扩/缩容

阿里前端高频vue面试题(边面边更)

bb_xiaxia1998

Vue

STM32L051测试 (五、串口测试 — 与Enocean模块通讯问题)

矜辰所致

stm32 串口通信 STM32L051 10月月更

在DAYU200上实现OpenHarmony视频播放器

OpenHarmony开发者

OpenHarmony

重磅 ! Redis+Nginx+JVM+设计模式+Spring全家桶+Dubbo

程序知音

Java 数据库 spring JVM 后端技术

前端培训怎么学习好就业?

小谷哥

数字化转型失败率高达84%?看看数字化转型方法对不对

雨果

数字化转型

Github星标57.9K!阿里巴巴Java面试突击汇总(全彩版)首次公开

Geek_0c76c3

Java 数据库 程序员 架构 开发

Java面试最强10W字面经,Github访问量破百万,火热涨星中!

Geek_0c76c3

Java 开源 程序员 面试 开发

全息投影正在威胁LED显示屏在舞台的地位

Dylan

LED显示屏 led显示屏厂家

电子表格也能做购物车?简单三步就能实现

葡萄城技术团队

前端 在线excel 应用系统 控件

数据分析师被当作取数机怎么办?

雨果

数据中台 数据分析师

消除两个开源项目之间长达4年的分叉

TiDB 社区干货传送门

TiDB 底层架构

vue面试之Composition-API响应式包装对象原理

bb_xiaxia1998

Vue

堡垒主机是堡垒机吗?两者有什么关系?

行云管家

网络安全 信息安全 堡垒机 堡垒机主机

vue这些原理你都知道吗?(面试版)

bb_xiaxia1998

Vue

行业大咖秀,第二期开播!

云计算

Hashtable、HashMap、TreeMap的区别

zarmnosaj

10月月更

堡垒机推荐厂商当属哪家?为什么?咨询电话多少?

行云管家

网络安全 信息安全 数据安全 堡垒机

2022年最新【Java经典面试800题】面试必备,查漏补缺;多线程+spring+JVM调优+分布式+redis+算法

Geek_0c76c3

Java 开源 程序员 架构 面试

《数据迁移》--单库迁移

TiDB 社区干货传送门

迁移

如何处理损坏的sst文件

TiDB 社区干货传送门

实践案例 管理与运维 故障排查/诊断

io模型

wzh

Linux io Linux Kenel IO模型

数据培训机构的学习费用是多少

小谷哥

How Good is TiDB as an HTAP System? A HATtrick Benchmark

TiDB 社区干货传送门

灵雀云全栈云原生开放平台ACP登陆VMware云市场

York

容器 云原生 数字化转型 虚拟化 应用现代化

springboot + redis多数据源 + jedis集群模式

try catch

redis springboot spring Boot Starter redis cluster redis多数据源

【Go微服务】开发gRPC总共分三步

王中阳Go

微服务 gRPC RPC #go 10月月更

都说复盘能力很重要,如何复盘更有效?Superset你值得拥有

王中阳Go

Code Review BI 分析工具 项目复盘 Superset 10月月更

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