Just Do It: 学习Sinatra

来源:互联网 发布:虚拟主机绑定域名 编辑:程序博客网 时间:2024/06/07 17:49
Sinatra 是一个基于Ruby语言,以最少代码快速创建Web应用为目的的DSL。本系列教程包含四部分,它将带你一步一步用Sinatra和DataMapper创建一个功能齐全的在线待处理事物Web应用---Just Do It。希望读者通过本系列教程了解Sinatra是如何快速、简单地创建Web app。下面开始我们的Sinatra基础之旅吧。

安装Sinatra
要使用Sinatra,你事先需要安装Ruby。这里建议使用RVM来安装。(你可以参考Glenn Goodrich的教程)。一旦你安装好了 Ruby和Rubygems,你就可以安装Sinatra了。用Rubygems来安装Sinatra不过是小菜一碟,你只需要打开终端并输入:

$> gem install sinatra

如此简单,你就做好了一切准备工作。

一个基础应用
现在你可以使用你喜欢的任一款文本编辑器,创建一个新的文本文件,名字是 "main.rb",然后输入下面这几行:

require 'sinatra'get '/' do "Just Do It"end

注意:如果你的Ruby版本低于1.9,需要在代码第一行添加:"require 'rubygems'"。
这就是一个基础的Sinatra应用:在文件头部,我们导入了Sinatra的Gem包。从第二行开始,是一个action。在Sinatra中,这叫做handler(处理器),因为它负责处理路由(route)和动作(action)。第二行最开始的部分(get)表明我们将使用哪一种HTTP method,在例子中,我们用的是Http GET,因为我们在视图“获得”某个页面。接下来的部分是有关路由的字符描述,即"/",这是本应用的根URL。代码块(code block)表明当用户访问这个URL时会发生什么。在这个简单的例子中,只是返回了一行文字“Just Do It”,这行文字最终会在浏览器渲染成页面。通常来说,handler代码块的最后一行总会在浏览器里渲染什么。
我们需要启动Sinatra服务器来看看这个例子是不是正常工作。打开终端,在main.rb所在的路径执行:

$> ruby main.rb

几秒钟后,你会看到这样的信息:

== Sinatra/1.2.6 has taken the stage on 4567 for development with backup from Thin

这时服务器已启动。在浏览器输入http://localhost:4567,页面就会显示一行“Just Do It”。至此,我们已创建了一个Sinatra应用。很简单吧?
内嵌模板
我们的Just Do It引用除了显示一行字符外没做其他事情。接下来我们会进一步扩展这个应用,这里我们先创建一些模板文件,他们包含HTML和用Ruby生成的动态内容。Slim是一个神奇的模板引擎,因此这部分工作变得很简单。在继续下面的内容之前,我们首先安装Slim的Gem包。

$> gem install slim

现在将main.rb的内容修改成如下内容:

require 'sinatra'require 'slim'get '/' do slim :indexend__END__@@layoutdoctype htmlhtml head meta charset="utf-8" title Just Do It link rel="stylesheet" media="screen, projection" href="/styles.css" /[if lt IE 9] script src="http://html5shiv.googlecode.com/svn/trunk/html5.js" body h1 Just Do It == yield@@indexh2 My Tasksul.tasks li Get Milk

首先,代码开始部分我们导入了Slim gem包,然后我们修改了handler,现在代码块的最后一行返回的是名为“index”的视图,它由slim产生。“@@index”之后的代码就是该视图。这就是Sinatra内嵌模板。我个人认为Sinatra一个杀手级特性就是它允许你把所所有代码存在一个文件里,这意味者所有的部分快速组织在一起。内嵌模板(inline template)位于“__END__"声明之后,每个模板以”@@“开头。

在代码中,我们还可以看到一个名为"@@layout"的模板。这个模板会随着每个视图被渲染,它提供了一个基本的HTML5脚手架。布局模板的关键在最后一行”==yield"。yield语句会对handler所请求的视图模板的内容进行渲染,在例子中,就是“index"。

这里所有的视图用的是Slim语法,这让编写HTML变得愉快多了,不过当心,Slim是对空格敏感的。嵌套的HTML元素应该是2个空格的样式缩进,Slim对该一致性要求非常严格。如果你不喜欢Slim,这里也有一大推其他模板语法可控选择,包括ERB,Haml和Markaby。

现在可以重启服务器,并重新加载http://localhost:4567的页面来看效果了。记住,每次修改代码之后,你都必须重启服务器。(如果你觉得重启比较麻烦,你可以试一下Shotgun,使用 gem install shotgun来安装)。

外部视图
现在你对 Sinatra 是如何使用 handler来渲染视图有所了解了。接下来我们看看如果不采用内嵌视图,该如何在文件夹中组织模板。

在开始之前,我们需要删除内嵌模板:打开 main.rb,然后删除 __END__ 定义及其后的所有模板。

在你保存 main.rb 文件的同一目录中,创建2个文件夹,一个名为“public”,另一个名为“views”。public文件夹用于保存公用的界面元素,比如图片和样式表。views文件夹则用于保存我们的所有 Slim 模板。上面例子中的模板现在会被保存到不同的文件中。首先将下面的代码保存在views文件夹中,名为 layout.slim。

doctype htmlhtml head meta charset="utf-8" title Just Do It link rel="stylesheet" media="screen, projection" href="/styles.css" /[if lt IE 9] script src="http://html5shiv.googlecode.com/svn/trunk/html5.js" body h1 Just Do It == yield

同样保存以下内容到index.slim
h2 My Tasksul.tasks  li Get Milk
现在最好重启服务器,然后看看我们的视图是不是还能正确被渲染。
动态内容
下面我们进一步来看看 Sinatra的一些特性。让我们创建一个新的handler用来接收一些动态的输入(main.rb):

get '/:task' do @task = params[:task] slim :taskend

你也许会注意到route项中包含了字符串“:task”,这是个命名参数,用冒号开头加以识别。命名参数从 URL 中获得值,可利用“params”哈希进行存取。在代码块的第一行,有一个名为‘@task’ 实例变量,它的值与“params[:task]”相等,内容就是写在 URL 斜杠后的一些东西。实例变量相当有用,因为它们可以在视图中被引用。
新的路由中指定了一个“task”视图,我们需要先创建它,将之以“task.slim”文件名保存到views文件夹中,内容如下:

h2 My Tasks= @task

这里用了“=”号来获得Ruby变量的值。Slim 会输出等号之后的 Ruby 运算结果。在例子中,则是 @task 实例变量的值,它与URL相匹配。重启服务器,然后在浏览器里查看 ‘http://localhost:4567/get-milk’,结果如图:
Just Do It: 学习Sinatra,第一部分 - 齐博云天 - 思想的自由是最高的独立
现在看起来不错,不过还有改进的空间,我们在 hanlder 里加了点新的代码美化一下页面:

get '/:task' do @task = params[:task].split('-').join(' ').capitalize slim :taskend

现在当你浏览 “http://localhost:4567/get-milk”,界面看起来是这样的:
 Just Do It: 学习Sinatra,第一部分 - 齐博云天 - 思想的自由是最高的独立
表单在结束这部分教程之前,我们来看一下如何通过表单来提交任务。打开 index.slim 文件,将其内容替换成
form action="/" method="POST"  input type="text" name="task"  input.button type="submit" value="New Task >>"
我们还需要一个 handler来处理提交上来的表单。如果你看了上面的代码,“action” 属性说明表单本身会被提交到URL ‘/’,而 “method” 属性该表单会使用POST方法。这直接引出 Sinatra 的第二个杀手级特性:如何在 handler 中指定请求方法。把下面的代码添加到 main.rb 中:

post '/' do
  @task =  params[:task]  slim :taskend

这段代码和我们之前用到的列出任务的 handler 非常相像,不同是现在我们通过表单的输入来获得任务。这个新的 handler 定义为POST路由,这意味这它只能响应HTTP POST请求。因此,我们可以为同一个路由“/” 定义两个 handler,并根据不同的请求类型赋予不同的代码段。
表单被提交的时候,它会将 params[:task] 的值设置为我们在页面输入的东西。用相同的办法,可以通过引用 params 哈希存取任意值。
你可以试着在http://localhost:4567/创建一些新的任务,不过现在我们还不能创建任务清单,也没有办法删除修改它们。我们需要某种方式来保存这些任务,这些内容我们会在本教程的第二部分中详述。
在本系列教程的第一部分,我们演示如何配置Sinatra并显示了几个页面。现在是真正有趣的部分,在本节中,我们将演示如何用数据库来保存任务。

这里我们会使用SQLite作为本地数据库,正如它的名字说表述的,这是一个轻量级的文件数据库,无需任何配置。如果你没有安装过这个数据库,可以参考本页的一些简单介绍。

为了和数据库交互,我们会使用DataMapper。这个ORM的工作方式和 Active Record 类似,但在方法和语法上略有不同。

为了配合SQLite使用DataMapper,下面的 Gem包需要被安装:

$> gem install data_mapper dm-sqlite-adapter sqlite3

我们需在 main.rb 文件的头部导入DataMapper:

require 'sinatra'require 'data_mapper'

要使用数据库,我们用了一行代码来设置其连接,告诉DataMapper连接到名为 “development.db”的SQLite数据库,该数据库文件将被保存在应用所在目录中。

DataMapper.setup(:default, ENV['DATABASE_URL'] || "sqlite3://#{Dir.pwd}/development.db")

注意:这里的DATABASE_URL是留给后面介绍部署到生产环境时使用PostGres时的URL。因为Heroku不支持SQLite数据库。因此这里的DATABASE_URL是Heroku部署时添加Postgres后,执行heroku addons:add heroku-postgresql 后返回的URL数据库连接URL
Task模型
为了将任务项保存到数据库中,我们需要创建一个task类。下面紧跟在 main.rb 的数据库连接代码之后:

class Task include DataMapper::Resource property :id, Serial property :name, String, :required => true property :completed_at, DateTimeendDataMapper.finalize

“include DataMapper::Resource” 这行代码将 DataMapper 的 Resource 模块 mixin 到 Task 类中,使它与 DataMapper 链接在一起。其他Ruby类也可以通过同样的方法成为 DataMapper 的资源。这行代码之后,是 Task 类的属性。:id 属性带有 Serial 类型赋予了每个任务一个自动增长的标识符。之后是任务实际需要的两个属性,即任务的名称(该项带有一个required属性)和完成时间。

添加任务
现在我们在 IRB 中来创建一些任务。打开一个命令行窗口,并执行 irb 命令( Ruby 安装路径需在PATH中)。
在开始添加任务之前,我们通过模型迁移(migratie)来创建 Tasks 表。使用下面的命令来完成:

$> irbruby-1.9.2-p180 :002 > require './main'DataObjects::URI.new with arguments is deprecated, use a Hash of URI components (/home/daz/.rvm/gems/ruby-1.9.2-p180/gems/dm-do-adapter-1.1.0/lib/dm-do-adapter/adapter.rb:231:in `new')=> true
ruby-1.9.2-p180 :003 > Task.auto_migrate!=> true

接下来添加一些任务。你可以在 irb 使用 create 方法来完成。如例子所示:

ruby-1.9.2-p180 :004 > Task.create(name: "get the milk")=> #ruby-1.9.2-p180 :005 > Task.create(name: "order books") => #ruby-1.9.2-p180 :006 > Task.create(name: "pick up dry cleaning")=> #ruby-1.9.2-p180 :007 > Task.create(name: "phone plumber")=> #

这些任务会被保存到数据库中。想要查看这些任务,我们需要简单修改一下 main.rb 处理根 URL 的 handler:

get '/' do @tasks = Task.all slim :indexend

Task.all 从数据库中获取所有的任务,并将它们以数组的形式保存在实例变量 @tasks 中。 如前所述,实例变量对所有的视图是可见的,因此我们可以在 index.slim 中遍历数组中的每个任务并以列表显示出来:

form action="/" method="POST" input type="text" name="task[name]" input.button type="submit" value="New Task >>"h2 My Tasksul.tasks - @tasks.each do |task| li.task= task.name

在命令行中输入 ruby main.rb 来启动服务器,刷新页面你就可以看到一个相当不错的任务列表。现在我们还需要从页面来添加任务,这并不难,因此我们已经有了一个类似的表单,只需要将它与 DataMapper 连在一起,这样任务就会被保存到数据库中去。
在上一节中,当表单被提交时,我们用下面的 handler 用来处理 POST 请求:

post '/' do @task = params[:task] slim :taskend

只需要如下做稍微的修改:

post '/' do Task.create params[:task] redirect to('/')end

随着表单提交的信息被保存在 params 哈希表用于创建一个新的任务。(在本例子中,只有任务的名字)然后页面被重定向到首页 (这时候会使用 GET 请求),所以会显示任务列表其中也包括我们最新添加的任务。在浏览器中尝试一下吧。

删除任务
到目前为止一切顺利,我们能为列表添加任务,现在的问题是如何删除它们呢?为此我们需要为每个任务添加一个删除的按钮,通过该按钮我们可以发送一个删除的请求到服务器上。在这儿,我打算将查看任务的视图代码分离到它自己的文件中。将下面的代码保存到views文件夹,文件名是 “task.slim”。

li.task id=task.id = task.name form.delete action="/task/#{task.id}" method="POST" input type="hidden" name="_method" value="DELETE" input type="submit" value="×" title="Delete Task"

现在将 index.slim 改为:

form action="/" method="POST" input type="text" name="task[name]" input.button type="submit" value="New Task >>"h2 My Tasksul.tasks - @tasks.each do |task| == slim :task, locals: { task: task }

注意:首先,在 task 视图,我们需要为删除按钮创建一个表单。为此我们包含了一个隐藏的值域,带有属性 name=”_method” value=”DELETE”。这是因为大部分浏览器不理解 HTTP 的 PUT 和 DELETE 动词,而了解 GET 和 POST。为了解决这个问题, Sinatra 使用隐藏的表单值域来“欺骗”浏览器以正确处理请求。这意味着当删除的表单被提交的时候,它能象DELETE请求一样被处理。

在 index 视图中,注意任务视图被以下面的形式被引用:

== slim :task, locals: { task: task }

这里演示了 Sinatra 如何处理局部模板(partial) – 你只需要嵌入调用一个模板即可。这例子中,我只需说明 task.slim 文件中使用的 task 变量与 block中传递的 task 变量的引用是相同的即可。

剩下来的哦就是写一个 handler 来删除任务。你可能注意到 action 属性里的 URL 形式是 /task/#{task.id}。对应的 handler 是这样的:

delete '/task/:id' do Task.get(params[:id]).destroy redirect to('/')end

这段代码简单地通过 URL 中的 id 查找被点击的任务,然后用 destroy 方法将它从数据库中删除。之后页面被重定向到根页面以列出剩下的任务。

结束任务
我们还需要能够结束任务。在我们创建 Task 类的时候,我们指定了一个 completed_at 属性来说明某项任务是什么时候完成的。如果这个属性值为空,我们可以假定该任务尚未被完成。打开 taks.slim,并做如下修改:

li.task = task.name form.update action="/task/#{task.id}" method="POST" input type="hidden" name="_method" value="PUT" -if task.completed_at.nil? input type="submit" value=" " title="Complete Task" -else input type="submit" value="?" title="Uncomplete Task" form.delete action="/task/#{task.id}" method="POST" input type="hidden" name="_method" value="DELETE" input type="submit" value="×" title="Delete Task"

在最后一个表单里包含了一个隐藏的表单域说明这是一个PUT 请求,这是因为我们正在修改任务类的属性(事实上,这里应该用 PATCH 请求,不过 Sinatra 1.3 尚未支持)。我们还根据任务是否结束显示了不同的按钮。我们还为任务的状态添加了一些视觉效果,比如结束的任务显示的是一个打勾的符号,而未结束的任务则显示的是空白。

注意:action 属性里给定的 URL 是完全相同的,这是因为 Sinatra 可以根据不同的 HTTP 动词来采取不同的动作。我们现在添加一个新的 handler来处理这个请求,在 main.rb 的尾部添加这些代码:

put '/task/:id' do task = Task.get params[:id] task.completed_at = task.completed_at.nil? ? Time.now : nil task.save redirect to('/')end

这里根据 URL里的 id来查找任务,如果 completed_at 属性尚未被设置,则将其设置为为当前时间,如果它已经被设置,则再次将其置空。换句话说,这个按钮充当了切换任务结束或者未结束状态的开关
重新启动服务器,尝试一下添加、删除、结束任务。用了还不到40行代码,我们就有了一个功能齐备的线上待办事项应用。不过现在还不是尽善尽美,我们在文章的第三部分再加以改进。
在本教程的第二部分,我们利用DataMapper将任务保存到后台数据库,同时我们还用Sinatra创建了Web前端以便显示、添加、删除和结束任务。现在这个 Just do it 应用功能齐备,但是样子还有点丑陋。

在本章中,我们继续对这个应用做些美化修饰,并增加一些功能,让你能够创建多个任务列表。

添加样式
现在我们用样式表来对应用进行美化修饰。在你的 “layout.slim” 中添加下面这行代码:

link rel="stylesheet" media="screen, projection" href="/styles.css"

现在创建一个名为” styles.css”的文件,并保存在”public”文件夹中,将下面 CSS 内容添加到该文件中:

.completed{ text-decoration: line-through; }.tasks{ padding:0; list-style:none; width:400px; }.task{ position:relative; padding:2px 0 2px 28px; border-bottom: dotted 1px #ccc;}form.update{ position:absolute; bottom:2px; left:0; }form.update input{ background:white; color:white; padding:0 2px; border:solid 1px gray; cursor:pointer;}.tasks li.completed form.update input{ color:#47FD6B; }form.delete{ display:inline; }form.delete input{ background:none; cursor:pointer; border:none; }

刷新页面,现在页面是不是看起来好多了,也更像一个真正的任务列表了。你也许注意到,该样式表中有个 ‘completed’ 类。 到目前为止,我们显示已完成的任务的方法是为它添加完成的时间,这样看起来不怎么样。我们来修改一下 task 视图,添加 ‘completed’ 类。这样利用样式表,已完成的任务看起来就是完全不同的风格。打开 “task.slim” 做如下修改:

li.task id=task.id class=(task.completed_at.nil? ? "" : "completed") = task.name form.update action="/task/#{task.id}" method="POST" input type="hidden" name="_method" value="PUT" -if task.completed_at.nil? input type="submit" value=" " title="Complete Task" -else input type="submit" value="?" title="Uncomplete Task" form.delete action="/task/#{task.id}" method="POST" input type="hidden" name="_method" value="DELETE" input type="submit" value="×" title="Delete Task"

关键在于头一行,该行使用了三元操作符来检查任务是否完成,并为完成的任务添加了 “completed” 类。 这个CSS会划掉已经完成的任务。如果再次启动应用,你就会觉得这样看起来更好也更自然 – 将任务标记成完成时还有某种视觉的效果。

任务列表
接下来我们让应用具备添加多个任务列表的功能。为此我们会创建一个 List 类, List 类里包含着许多任务。 DataMapper利用关联在 List 类和 Task 类之间创建关系。我们在每个类的尾部添加一行代码。Task 模型使用 belongs_to 声明,而 List 模型则使用 has n 声明。在后台,这些声明会为 Task 类添加一个 list_id 属性,用来跟踪这个任务是属于哪个列表,但是我们不需要直接访问这个属性。每个类都会获得一些额外的方法,因此你可以通过 List.tasks 来获得某个列表的任务,或者通过 Task.list 来获得任务所在的列表。打开 main.rb,新增一个 List 类,对并 Task 类做如下修改:

class Task include DataMapper::Resource property :id, Serial property :name, String, :required => true property :completed_at, DateTime belongs_to :listendclass List include DataMapper::Resource property :id, Serial property :name, String, :required => true has n, :tasks, :constraint => :destroyendDataMapper.finalize

由于我们创建新的模型,我们需要更新底层的数据库,这可以通过 DataMapper 的 auto_migrate! 方法来完成。在控制台窗口中打开 irb :

$> irbrequire './main'DataMapper.auto_migrate!

注意:这个操作会清空数据库中原有的所有任务。

增加删除列表
接下来我们创建一些处理器来处理任务列表。这些处理器与文章第二部分的 task 处理器类似,这里我们只列出全部代码,请将它们保存在 main.rb 的后面:

post '/new/list' do List.create params['list'] redirect to('/')enddelete '/list/:id' do List.get(params[:id]).destroy redirect to('/')end

这些代码相当直观:一个处理器基于表单提交的参数创建新的任务清单,另一个依据 URL 中的 id 删除任务清单。接下来我们对 index 页面的表单做点小修改,使它可以用于创建清单而不是任务。

form.new action="/new/list" method="POST" input type="text" name="list[name]" input type="submit" value="+ List"ul.lists - @lists.each do |list| == slim :list, locals: { list: list }

这个视图包含了一个实例变量 @lists,它代表了数据库中的所有的列表,该变量仍未被赋值,所以我们需要更新相关的处理器,在 main.rb 中找到和根 URL 对应的处理器,如下:

get '/' do @tasks = Task.all slim :indexend

将它改为:

get '/' do @lists = List.all(:order => [:name]) slim :indexend

这段代会查找所有的列表而不再是所有的任务。 We will get the tasks from the database on a list by list basis.

Index 视图的最后一段指向了一个名为 list 的视图,我们我们就来创建它。该视图用于显示每个列表中的任务。创建一个新的文本文件,并将下面的内容保存在 view 文件夹下的 list.slim 文件中。

li.list h2= list.name form.new action="/#{list.id}" method="POST" input type="text" name="task[name]" ul.tasks - list.tasks.each do |task| == slim :task, locals: { task: task } form.destroy action="/list/#{list.id}" method="POST" input type="hidden" name="_method" value="DELETE" input type="submit" value="×"

这段代码和 index 视图中的原有代码十分相近。开头部分列出了列表的名字,接下来是一个表单,用来添加任务。紧随其后的是任务清单,使用了和本文第二部分的 task 视图一样的代码。代码的最后一段是用来访问 delete 处理的表单,这样列表就可以被删除掉了。

将任务添加到列表中
我们现在还要做点修改以确保任务被正确地添加。任务属于某个列表,因此需要确保指定了任务要添加到哪一个列表中。通过在创建任务的 URL 中指定了列表的 id ,我们做到了这一点。注意 list.slim 中的这一行:

form.new action="/#{list.id}" method="POST"

改行指定了处理表单 post 的 action 必须包含该表单所在的列表的 id .现在我们用下面的处理器来添加任务:

post '/' do Task.create params['task'] redirect to('/')end

需要做如下变更:

post '/:id' do List.get(params[:id]).tasks.create params['task'] redirect to('/')end

该处理器获取 URL 中指定的列表的 id ,并在数据库中找到该列表,之后用表单中的参数创建了属于该列表的新任务。

更多的样式
在本文将要结束之际,我们要做的就是为列表再添加一些样式。打开 style.css ,添加下面这几行:

.lists{ padding:0; list-style:none; overflow:hidden; }.list{ float: left; width:23%; margin:0 1%; border-top:solid 5px #ccc; }

你还需要确保下面这行已经删除:

width:400px;

这就是我们所做的!现在你已经有了一个功能齐整的 To Do list 应用。在本系列文章的最后一部份,我们将用 SASS 添加更多的样式表,并用 Heroku 将它部署到 Web 上。

这是本系列文章的最后一部分。本教程的目标是带领从未用过Sinatra的用户从头开始创建一个应用,并将之最后发布。

到前三部分结束时,我们已经完成了 To Do List 应用的大部分功能。在本系列的最后一部分,我们将先看看如何用SaSS来编写CSS ,之后将本应用发布到 Heroku 云服务上。

Logo
每个上点档次的应用都应有一个 logo,所以我创建了一个简单的“tick” logo,你可以在GitHub上找到。我们用它来作为本应用的头条的背景图片。打开 Styles.css 并添加下列CSS:

h1.logo{  font-family: helvetica,arial,sans-serif;  font-size: 24px;  color: white;  padding: 64px 0 0;  margin: 0 auto;  text-transform: uppercase;  text-align: center;  font-weight: normal;  letter-spacing: 0.3em;  background: transparent url(/logo.png) 50% 0 no-repeat;  }

(注意 – 也许你还发现,你需要为 layout.slim 的 h1 元素添加一个 ‘logo’ 类,这段 CSS 才会起作用。)

我还用这个 logo 创建了个网站小图标(favicon),你也可以在 Github 上找到。为了显示这个网站小图标,你需要在 html 中添加一个链接,所以 layout.slim 最后看起来是这样的:

doctype htmlhtml head meta charset="utf-8" title Just Do It! link rel="shortcut icon" href="/favicon.ico" link rel="stylesheet" media="screen, projection" href="/styles.css" /[if lt IE 9] script src="http://html5shiv.googlecode.com/svn/trunk/html5.js" body h1.logo Just Do It! == yield

Getting Sassy
Sass (Syntactically Awesome Stylesheets) 是一种 CSS 模板语言,你可以用 Sass 来编写样式表,该样式表会在服务器端被编译成 CSS 并发送到浏览器。Sass 有2个变种,原有的 “Indented Sass”和后来的“SCSS”(Sassy CSS)。这儿我打算用 SCSS,因为它看起来更象普通的 CSS 。不过你还应该两种都尝试下看那种更适合你。

要把 Sass 集成到 Sinatra 项目中,我们需要安装 Sass gem:

$> gem install sass

接下来,我们在 main.rb 的尾部添加下列处理器:

get '/styles.css' do scss :stylesend

现在我们拿掉原有的布局文件 Styles.css 的引用,取而代之的是名为“styles.scss”的 Sass 文件。目前这个文件还不存在,我们马上会创建它。这个文件应保存在 “views” 文件夹。将 styles.css (在 public 文件夹中)的内容拷贝到 styles.scss 中,然后删除 styles.css。

如果你在浏览器里看一下我们的应用,并没有什么变化。这是因为 SCSS 是 CSS 的一个超集,因此任何正确的 CSS 都能正常工作,我们上面把标准 CSS 的内容搬到 SCSS 中,但现在我们将要大幅修改我们的样式表了。

CSS 变量
人们最希望 CSS 有的功能之一是变量。 无需等待,Sass 让我们可以马上使用变量。例如我们可以用变量表示颜色,并在标记中引用。

$orange:#D78123;$blue:#1E1641;$green:#02d330;$grey: #999;

现在我们可以在样式表的其余部分引用这些颜色。例如将每个清单的头部改为桔色,Styles.scss 可以这样写:

.list h1{ color: $orange;}

这大大提高了可维护性,如果很多元素都是用该颜色,我们需要颜色加深时,我们只需要在一处修改就可以了。有了变量,你可以做的事情还很多,包括数学运算,你可以参考有关文档。

CSS Mixins
Mixins 是 Sass 相当酷的功能,让你可以创建模块化和可重用的 CSS 。例如 我们创建了一个名为 button 的 mixin ,它包含了有关的 CSS 可以将标准链接变为按钮。

@mixin button($color){ background:$color; border: 2px solid darken($color,15%); cursor: pointer; color: #fff; width: 150px; margin-left:4px;}

接下来我用 @include 语句将这些通用样式添加到样式表的其他 CSS 声明中:

.button{ @include button($orange); }

这会把 button mixin 的所有 CSS 添加到具有”button”类的所有元素中。当然我们可以在 “button” 类中直接编写 CSS,但是 mixin 允许我们将其添加到任何其他的 CSS 声明中而无需任何重复代码。注意我们的 mixin 带有一个 color 参数,允许我们无需重复任何代码就能创建不同颜色的按钮。同时我们还使用前面创建的变量 $orange 来创建桔色按钮。

嵌套 CSS
Sass 另一个最棒的功能是能到嵌套你的声明。这可以大大减少你打字的时间,并为你的 CSS 文件呈现更强的结构化。

例如,下面 CSS 被用来样式化每个清单的列表和标题:

.list{ float: left; background: #fff;}.list h1{ color:$orange;}

通过在 .list 声明嵌套 h1 声明里,可以将上面的代码写地更有效率,如下:

.list{ float: left; background: #fff; h1{ color:$orange; }}

编译之后,上面的 Scss 得到和上面一样的 CSS,但它采用了嵌套结构,显示上更为简洁。如果你用更深层次的嵌套 CSS,还可以节约很多打字的时间。

Sass 的功能如此强大,现在编写 CSS 变得简单多了。在 Sinatra 中采用 Sass 也如此简单,没有什么理由不在所有的项目中使用它。特别是你可以只先标准的 CSS,然后在需要的时候,在加入额外的“时髦”的功能。上面举的例子只是初步展示了 Sass 的一些粗浅的功能。读者可以通过阅读文档、更重要地编写自己的代码来感受 Sass。我相信一旦你开始用 Sass ,你就不会再用回普通的 CSS!

我们无法在这里列出完整的 Scss 文件内容,你可以在 Github 得到完整代码。现在美化应用的阶段大功告成,是时候发布应用了。

Heroku
Heroku 提供了特别棒的服务,能让用户在云端托管 Sintra 应用。它对于基本的站点是免费的,包括数据库存储,当你的应用变得流行时,还可以进行扩容。它消除了托管网站的所有麻烦,可以用在大型的可伸缩的实际运作的网站上。Heroku 还提供了优秀的文档与支持。

首先你要做的是,就是获得一个 Herkou 帐号,如果你还没有的话。接下来,确认你是否在系统里安装了Git。如果还没有的话,Github 上提供了很有用的指导。请特别注意有关设置 SSH 密匙的章节,你需用它来访问 Heroku。

下一步,你将要安装 Heroku gem ,用来和你发布在 Heroku 上的应用交互:

$> gem install heroku

如果你是首次访问 Heroku, 你需要添加你刚创建 SSH 密钥(只需要做一次):

$> heroku keys:add

现在我们在 Heroku 上创建应用:

$> heroku create justdoit

(注意,你可能需要为你的应用取一个不同的名字,因为 justdoit 已经被我占用了!)

现在你配置好了 Heroku ,可以开始发布应用。

Bundler
在把应用发布到 Heroku 上之前,我们需要要用 Bundler 来管理应用的所有依赖。这包括我们所使用的所有 gems ,还有这些 gems 依赖的 gems。 Bundler 将所有这些 gems 直接安装到应用的目录。这意味着你的应用现在“捆绑”了所有的依赖,而无需依赖于服务器是否默认安装所需的东西。(Heroku 默认根本不提供任何 gem)。你还可以管理 gem 的版本号因此你可以确保上线时使用的版本号和开发时一样。

为此我们需要安装 Bundler gem:

$> gem install bundler

现在我们要在应用的目录中创建两个新的文件。最重要的一个,我们需要一个 Gemfile 列出所有使用的 gem 的清单。将下面的代码保存在应用的根目录下,名为 Gemfile(无扩展名):

source :rubygemsgem "sinatra"gem "datamapper"gem "slim"gem "sass"gem "dm-postgres-adapter", :group => :productiongem "dm-sqlite-adapter", :group => :development

最后两行指定了数据库适配器,Datamapper 用之连接数据库。目前在开发机上我们使用的是 Sqlite,但 Heroku 使用 PsotgreSQL。如何根据环境的不同而使用不同的数据库,这可以通过在 Bundler group 配置不同的数据库适配器 gem 来实现。

我们还需要 Rackup 文件。打开你的文本编辑器,将下面代码保存为 config.ru:

require 'bundler'Bundler.requirerequire './main'run Sinatra::Application

最后你要做的是在本地测试所做的修改,我们运行 bundle install 命令,它会创建 .bundle 目录,里面包含所有在 Gemfile 中列出的 gem。

bundle install --without production

–without production 标志确保任何在production group 里的 gem 不会被安装。

你无需产品环境中运行这个命令,Heroku 检测到 Gemfile 就会自动运行该命令。不过我们必须告诉 Heroku 避免使用任何“开发”组里的 gem,通过下面简单的命令即可完成:

heroku config:add BUNDLE_WITHOUT="development:test" --app justdoit

(注意 你应该是使用你应用自己的名字而不是 justdoit)

在我们开始发布应用之前,我们应该在本地机器上检查一切运行正常。这需要通过另一种方式来完成,现在我们该用上 rackup 文件了,打开命令行窗口,输入下面命令:

$> rackup

现在在端口 9292 上会启动服务器。访问 http://localhost:9292/ 就可以看到它。

发布
现在剩下的就是把应用发布到 Heroku。首先我们需要创建一个名为 .gitignore 的文件,将之保存在根目录,内容是以下2行:

development.db.bundle

这可以防止任何不必要的文件被添加到 git 仓库中。我们不想 Sqlite 数据库文件被放到 git 仓库因为 Heroku 不需要它。我们也不想 .bundle 目录被发送到生产服务器上(它有自己的版本).

接下来的工作是初始化 Git 仓库:

git initgit add .git commit -m 'initial deployment'

最后我们把应用推送到 Heroku.

git push heroku master

一旦你开发你自己的应用,非常容易更新 Heroku 上的代码。用下面的命令,很简单就能将代码提交到 Git 仓库然后将其推送到 Heroku上:

git add .git commit -m 'some message about the updates'git push heroku master

注意:在推送之前,检查main.rb中数据库setup部分的代码,将DATABASE_URL改为你addon的数据库调用URL。下面是我的数据库连接代码:

DataMapper.setup(:default, ENV['HEROKU_POSTGRESQL_IVORY_URL'] || "sqlite3://#{Dir.pwd}/development.db")

最后要做的就是通过运行迁移在服务器上配置数据库。 Hereko 命令行界面允许你访问 irb 控制台以便和你的应用交互:

$> heroku consoleRuby console for justdoit.heroku.com>> require './main'>> DataMapper.auto_migrate!

你可以从命令行打开站点:

heroku open

这会带你跳转到你的线上应用。你可以看到我最后完成的 Just Do It ! 请随意体验一下。我已经把所有的源代码放到 Github 上,你可以查看所有代码的全貌。

是时候结束本系列文章了。

我们来为这系列文章写一个结论。我希望它能给你足够的信息让你开始使用 Sinatra 并渴望了解更多。学习本教程的最好方法是自己动手 Sinatra 入门的起点是如此的低,因此开始创建一个新的应用并把弄代码是如此的简单。

Just Do It: 学习Sinatra,第一部分

Just Do It: 学习Sinatra,第二部分

Just Do It: 学习Sinatra,第三部分

注:本文是翻译自 http://rubysource.com/just-do-it-learn-sinatra-i 的系列文章。我按照英文学习了Sinatra,并部署到Heroku上。这是我部署后的站点地址:http://planws.herokuapp.com/
0 0
原创粉丝点击