`
mypages
  • 浏览: 90306 次
  • 性别: Icon_minigender_1
  • 来自: 深圳
社区版块
存档分类
最新评论

应用Rails进行REST开发(六)

阅读更多

自定义 Action

我们已经知道,在 /config/routes.rb 里定义的路由,会自动生成对资源的CRUD的操作。但是我们如何处理那些并不是CRUD的操作?下面我们就用一个例子来说明这一点。例如我们在 ProjectController里有一个close的方法。这个close并不是真正的删除一个资源,而只是把给这个资源设置一个标志:表示这个
资源被关闭了。

首先修改一下数据库:
> ruby script/generate migration add_closed_to_projects
exists db/migrate
create db/migrate/003_add_closed_to_projects.rb
Listing 1.16: ontrack/db/migrate/003 add closed to projects.rb
class AddClosedToProjects < ActiveRecord::Migration
def self.up
add_column :projects, :closed, :boolean, :default => false
end
def self.down
remove_column :projects, :closed
end
end
rake db:migrate


现在,我们在IteratinController的index.rhtml上创建一个 close 的链接。


Listing 1.17: ontrack/app/views/projects/index.rhtml
<% for project in @projects %>
<tr id="project_<%= project.id %>">
<td><%=h project.name %></td>
<td><%= link_to "Show", project_path(project) %></td>
...
<td><%= link_to "Close", <WHICH_HELPER?> %></td>
</tr>
<% end %>


现在有2个问题摆在我们面前:
1.使用 http 协议的哪个动作来发送这个请求呢?
2.对于这个链接,该如何生成那些 helper方法呢?


因为这个 close 动作并不是CRUD中的任何一个,所以Rails 也不知道该用http的哪个来做这个事情。不过既然 close 也是 update 中的一种,所以应该使用post来发送这个请求。我们还是得在 /config/routes.rb 里定义这个路由,当然定义完路由之后,就会有相应的path和url的helper方法了。因为这个close的操作,仍然是针对projects 这个资源的,所以,我们可以在定义路由的时候,使用一个名字叫“member”的hashmap,这个hashmap 的key,就是自定义action的名字,hashmap的value,就是所使用的http的动作。

map.resources :projects, :member => { :close => :post }


hashmap 的value可以使用 :get, :put, :post, :delete, :any。如果使用了:any,那么可以用http的任何动作来发送这个请求。


定义完这个路由后,我们就可以使用helper方法了:
<td><%= link_to "Close", close_project_path(project) %></td>


因为我们定义的是“:member => { :close => :post }”,所以,这个请求只能以post的方式来发送,如果使用其它方式如“get”,那么请求就是无效的。为了安全起见,我们还是把它改成用按钮的方式来发 送,幸运的是我们可以使用Rails 提供的button_to 来做这件事情:


<td><%= button_to "Close", close_project_path(project) %></td>
=>
<td>
<form method="post" action="/projects/1;close" class="button-to">
<div><input type="submit" value="Close" /></div>
</form>
</td>


现在我们要做的就是写完 ProjectController中的 close 方法:
Listing 1.18: ontrack/app/controllers/projects controller.rb
def close
respond_to do |format|
if Project.find(params[:id]).update_attribute(:closed, true)
flash[:notice] = "Project was successfully closed."
format.html { redirect_to projects_path }
format.xml { head :ok }
else
flash[:notice] = "Error while closing project."
format.html { redirect_to projects_path }
format.xml { head 500 }
end
end
end


除了“:member”,我们还可以使用“:collection”,“:new”。
“:collection”的用途是:所操作的资源不是一个,而是很多个。下面是一个用“:collection”方式得到一个资源的列表的例子:
map.resources :projects, :collection => { :rss => :get }
--> GET /projects;rss (maps onto the #rss action)


所以,有的时候,“:member”更多的是更新一个资源,而“:collection”是得到一堆资源。
对于“:new”,一般用于那些还没有被保存的资源:
map.resources :projects, :new => { :validate => :post }
--> POST /projects/new;validate (maps onto the #validate action)

我们是否仍然“DRY”(Don’t Repeat Yourself)?

我们是否为了“DRY”原则?似乎是这样的:我们不仅在controller里定义了action,同时在 /config/routes.rb 里也定义了一遍。


作为替换REST风格的调用的方式,您可以用传统的方式来调用一个方法:
<%= link_to "Close", :action => "close", :id => project %>


但是别忘了,即使用传统的方式,你也得在/config/routes.rb里定义一个路由:“map.connect ’:controller/:action/:id’”。

自定义信息格式

目前 respond_to 可以返回如下的信息格式:
respond_to do |wants|
wants.text
wants.html
wants.js
wants.ics
wants.xml
wants.rss
wants.atom
wants.yaml
end


你可以通过增加新的MIME类型的信息来扩展这个功能。假设您已经开发了一个“PIM”应用系统,现在你希望把地址信息用”vcard” 格式来传送。要实现这一共能,首先你需要注册新的信息格式在
/config/environment.rb。
Mime::Type.register "application/vcard", :vcard然后,我们来修改一下show action,使得返回的信息以vcard 的格式来传送。


def show
@address = Address.find(params[:id])
respond_to do |format|
format.vcard { render :xml => @address.to_vcard }
...
end
end


这个 to_vcard 方法不是 ActiveRecord 的标准方法,所以必须按照 vcard的标准来实现(RFC2426)。如果实现正确的话,那么通过下面的URL,就可以得到正确的信 息:“http://localhost:3000/addresses/1.vcard”。

在REST 中使用AJAX

在REST风格的系统中使用AJAX?非常简单,可以说这一小节没什么新鲜的玩意要学习。您还是使用以前所使用的 remote 系列的helper 方法,只不过传递的参数需要改变,现在使用 path helper 方法,而不是以前所使用的 contoller,action 。下面的例子会让您更清晰的明白这一点:


link_to_remote "Destroy", :url => project_path(project),
:method => :delete
=>
<a href="#" onclick="new Ajax.Request("/projects/1",
{asynchronous:true, evalScripts:true, method:"delete"});
return false;">Async Destroy</a>


给您提醒一下:千万千万记得导入相应的ajax javascript 文件,不然当您的ajax无效,而气得把键盘砸坏的时候,我们就无能为力了。导入相应的javascript 文件相当的简单:


Listing 1.19: ontrack/app/views/layouts/projects.rhtml
<head>
<%= javascript_include_tag :defaults %>
...
</head>


这个“Destroy”链接将会调用ProjectsController的destroy方法。从逻辑上来说现在一切正常:用户点这个链接,系统删除相 应的资源。不过我们还是漏了一点:在 respond_to 中,我们应该增加新的返回类新,也就是javascript类型。

Listing 1.20: ontrack/app/controllers/projects controller.rb
def destroy
@project = Project.find(params[:id])
@project.destroy
respond_to do |format|
format.html { redirect_to projects_url }
format.js # default template destroy.rjs
format.xml { head :ok }
end
end


可以看出来,唯一的改变就是增加了“format.js”。因为这个“format.js”并不是一个要被执行的代码块,所以,Rails 会按照标准显示destroy.rjs。


Listing 1.21: ontrack/app/views/projects/destroy.rjs
page.remove "project_#{@project.id }"


这个 rjs 文件从当前的浏览页面中删除了 “project_ID”这个DOM元素,为了让这个删除起到效果,我们就需要在显示 project 上进行修改:
Listing 1.22: ontrack/app/views/projects/index.rhtml
...
<% for project in @projects %>
<tr id="project_<%= project.id %>">


这是一个遵循DRY原则和减少对当前系统的修改的一个好例子!也体现了REST的优势,只需要在controller里增加一行,就可以处理javascript请求了。

同时也告诉了我们一个REST编程的原则:在 respond_to 外实现逻辑处理,能够极大地降低重复的代码。

测试

不管开发REST风格的应用是多么的让我们激动,我们也不能忘记最重要的一个朋友:测试!
之前我们写了那么多代码,但是一次单元测试测试都没运行过!下面我们来运行一下吧!
> rake
...
Started
EEEEEEE.......


好消息是,所有的单元测试和功能测试都可以运行。坏消息是,关于IterationsController的7个功能测试,全部失败!

如果测试用例抛出异常,那么很明显的—这里存在一些错误。我们遇到的错误也很明显:所有的IterationsController的测试用例都是 scaffold 来生成的,并没有一个“父”资源的关联—还记得吗,我们已经让iterations 资源成为了projects 资源的“子”资源。


为了让我们的测试用例通过,我们必须给每个测试方法都增加project_id。例如:
Listing 1.23: ontrack/test/functional/iterations controller test.rb
def test_should_get_edit
get :edit, :id => 1, :project_id => projects(:one)
assert_response :success
end


当然了,你需要加载必要的fixtures:
fixtures :iterations, :projects


改完全部的测试用例以后,我们发现还是有2个测试用例无法通过:

test_should_create_iteration
test_should_update_iteration

失败的代码来自这行“assert_redirected_to iteration_path(assigns(:iteration))”。

错误是非常显然的:我们已经知道iteration_path的第一个参数应该是project id。我们同样需要修改一下:
assert_redirected_to iteration_path(projects(:one), assigns(:iteration))


另外,在使用 redirect 断言的时候,path helper 方法的使用,是REST和非REST风格应用的唯一区别。

REST 风格的客户端:ActiveResource

我们总是把 ActiveResource 和 REST一起提及。ActiveResource 是一个Rails 的库,用来开发基于REST的WEB服务客户端。这种基于REST的客户端,也是适用 http 的4个标准的动作来和服务器通信。


ActiveResource 并不是Rails 1.2 的一部分,但是您可以使用svn 从网站下载它的代码:
> cd ontrack/vendor
> mv rails rails-1.2
> svn co http://dev.rubyonrails.org/svn/rails/trunk rails


ActiveResource把客户端资源抽象成一个类,这个类继承自ActiveResource::Base。例如通过下面的例子,我们来调用服务器上的project 资源:
require "activeresource/lib/active_resource"
class Project < ActiveResource::Base
self.site = "http://localhost:3000 "
end


可以看到,我们导入了ActiveResource 的库,然后,服务器的地址赋给了类的变量 site。这个 Project 类,把服务器上的资源抽象成了一个客户端的类,这就让开发人员觉得他们就好像操作一个ActiveRecord 一样。

例如,我们用一个project id 和 find 方法去请求服务器上的一个资源:
wunderloop = Project.find 1
puts wunderloop.name


这个 find 方法会执行标准的GET动作:GET /projects/1.xml


然后服务器返回xml格式的信息。客户端把xml信息转化成一个ActiveResource对象 wunderloop,就好像一个ActiveRecord对象,可以得到和改变它的任何属性。那么我们如何去更新一个资源呢?

wunderloop.name = "Wunderloop Connect"
wunderloop.save


save 方法会是用put 动作向服务器传递信息。
PUT /projects/1.xml


刷新一下你的浏览器看看,那条记录肯定被改变了。和 find,save一样简单,创建一个新的资源也是非常方便:
bellybutton = Project.new(:name => "Bellybutton")
bellybutton.save


新的资源将会以post方式传递给服务器,并且保存到数据库里。
POST /projects.xml


刷新浏览器,会看到新建立的资源。最后,我们来看一下删除一个资源。
bellybutton.destroy


destroy方法将会以DELETE方式发送给服务器,并且删除这个资源。
DELETE /projects/2.xml


ActiveResource 使用http的4个动作来和服务器交互。对于REST的资源,它提供了非常好的抽象,此外,在ActiveRecord中的许多方法,在ActiveResource中仍然找得到。例如查找一个资源的全部记录:
Project.find(:all).each do |p|
puts p.name
end


相信使用ActiveResource可以开发出很好的松耦合的系统,我们不如马上去下载ActiveResource的代码,亲自体验一下吧!

大结局

这个世界并不是非得需要REST。有很多的解决方案可以考虑,并且可以很容易实现。大概更多的时候,是您可能现在正处于某个项目的中期,这时,您发 现了Rails这个新特性。我想如果您此时就开发一个单独的模块,并且使用REST的风格,是毫无问题的。如果您要准备开始一个全新的项目,那么不妨考虑 一下使用
REST,理由十分明显:清晰的架构,更少的代码,多个客户端的支持。

终于结束了,有关rails2.0 rest风格的新特性建议看一篇文章叫"Rolling with Rails 2.0 - The First Full Tutorial "。http://www.iteye.com/topic/162536

分享到:
评论

相关推荐

    基于智能温度监测系统设计.doc

    基于智能温度监测系统设计.doc

    搜广推推荐系统中传统推荐系统方法思维导图整理-完整版

    包括userCF,itemCF,MF,LR,POLY2,FM,FFM,GBDT+LR,阿里LS-PLM 基于深度学习推荐系统(王喆)

    2023-04-06-项目笔记 - 第三百五十五阶段 - 4.4.2.353全局变量的作用域-353 -2025.12.22

    2023-04-06-项目笔记-第三百五十五阶段-课前小分享_小分享1.坚持提交gitee 小分享2.作业中提交代码 小分享3.写代码注意代码风格 4.3.1变量的使用 4.4变量的作用域与生命周期 4.4.1局部变量的作用域 4.4.2全局变量的作用域 4.4.2.1全局变量的作用域_1 4.4.2.353局变量的作用域_353- 2024-12-22

    和美乡村城乡融合发展数字化解决方案.docx

    和美乡村城乡融合发展数字化解决方案.docx

    CNN基于Python的深度学习图像识别系统

    基于Python的深度学习图像识别系统是一个利用卷积神经网络(CNN)对图像进行分类的先进项目。该项目使用Python的深度学习库,如TensorFlow,构建和训练一个模型,能够自动识别和分类图像中的对象。系统特别适合于图像处理领域的研究和实践,如计算机视觉、自动驾驶、医疗影像分析等。 项目的核心功能包括数据预处理、模型构建、训练、评估和预测。用户可以上传自己的图像或使用预定义的数据集进行训练。系统提供了一个直观的界面,允许用户监控训练进度,并可视化模型的性能。此外,系统还包括了一个模型优化模块,通过调整超参数和网络结构来提高识别准确率。 技术层面上,该项目使用了Python编程语言,并集成了多个流行的机器学习库,如NumPy、Pandas、Matplotlib等,用于数据处理和可视化。模型训练过程中,系统会保存训练好的权重,以便后续进行模型评估和预测。用户可以通过简单的API调用,将新的图像输入到训练好的模型中,获取预测结果。

    拳皇97.exe拳皇972.exe拳皇973.exe

    拳皇97.exe拳皇972.exe拳皇973.exe

    基于python和协同过滤算法的电影推荐系统

    基于python和协同过滤算法的电影推荐系统 基于python和协同过滤算法的电影推荐系统基于python和协同过滤算法的电影推荐系统基于python和协同过滤算法的电影推荐系统基于python和协同过滤算法的电影推荐系统基于python和协同过滤算法的电影推荐系统基于python和协同过滤算法的电影推荐系统基于python和协同过滤算法的电影推荐系统基于python和协同过滤算法的电影推荐系统基于python和协同过滤算法的电影推荐系统基于python和协同过滤算法的电影推荐系统基于python和协同过滤算法的电影推荐系统基于python和协同过滤算法的电影推荐系统基于python和协同过滤算法的电影推荐系统基于python和协同过滤算法的电影推荐系统基于python和协同过滤算法的电影推荐系统基于python和协同过滤算法的电影推荐系统基于python和协同过滤算法的电影推荐系统基于python和协同过滤算法的电影推荐系统基于python和协同过滤算法的电影推荐系统基于python和协同过滤算法的电影推荐系统基于python和协同过滤算法的电影推荐系统基于python和协同过滤算法

    DEV-CPP-RED-PANDA

    DEV-CPP-RED-PANDA

    Python语言求解旅行商(TSP)问题,算法包括禁忌搜索、蚁群算法、模拟退火算法等

    Python语言求解旅行商问题,算法包括禁忌搜索、蚁群算法、模拟退火算法等。

    pdfjs2.5.207和4.9.155

    pdfjs 用于在浏览器中查看/预览/打印pdf。 pdfjs 2.5.207 支持firefox/chrome/edge/ie11以上版本。 如果需要支持旧版本浏览器,可以使用这个,是未修改过的原版,支持打印和下载按钮。亲测有效。 pdf 4.9.155分两个包: pdfjs-4.9.155-dist.zip pdfjs-4.9.155-legacy-dist.zip

    建设项目现场高温人员中暑事故应急预案.docx

    建设项目现场高温人员中暑事故应急预案

    数据结构上机实验大作业-线性表选题.zip

    数据结构上机实验大作业-线性表选题.zip

    基于高德地图的校园导航全部资料+详细文档+高分项目.zip

    【资源说明】 基于高德地图的校园导航全部资料+详细文档+高分项目.zip 【备注】 1、该项目是个人高分项目源码,已获导师指导认可通过,答辩评审分达到95分 2、该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的,请放心下载使用! 3、本项目适合计算机相关专业(人工智能、通信工程、自动化、电子信息、物联网等)的在校学生、老师或者企业员工下载使用,也可作为毕业设计、课程设计、作业、项目初期立项演示等,当然也适合小白学习进阶。 4、如果基础还行,可以在此代码基础上进行修改,以实现其他功能,也可直接用于毕设、课设、作业等。 欢迎下载,沟通交流,互相学习,共同进步!

    全自动批量建站快速养权重站系统【纯静态html站群版】:(GPT4.0自动根据关键词写文章+自动发布+自定义友链+自动文章内链+20%页面加提权词)

    【静态站群程序视频演示,只有视频,不含程序,下载须知】【静态站群程序视频演示,只有视频,不含程序,下载须知】全自动批量建站快速养权重站系统【纯静态html站群版】:(GPT4.0自动根据关键词写文章+自动发布+自定义友链+自动文章内链+20%页面加提权词)

    9.30 SWKJ 男头7张+女头2张.zip

    9.30 SWKJ 男头7张+女头2张.zip

    基于java+springboot+vue+mysql的技术交流和分享平台 源码+数据库+论文(高分毕业设计).zip

    项目已获导师指导并通过的高分毕业设计项目,可作为课程设计和期末大作业,下载即用无需修改,项目完整确保可以运行。 包含:项目源码、数据库脚本、软件工具等,该项目可以作为毕设、课程设计使用,前后端代码都在里面。 该系统功能完善、界面美观、操作简单、功能齐全、管理便捷,具有很高的实际应用价值。 项目都经过严格调试,确保可以运行!可以放心下载 技术组成 语言:java 开发环境:idea、vscode 数据库:MySql5.7以上 部署环境:maven 数据库工具:navicat

    一个通过单片机在各种屏幕上显示中文的解决方案.7z

    一个通过单片机在各种屏幕上显示中文的解决方案.7z

    Halcon模板匹配图像包

    图像

    线上辅导班系统-JAVA-基于springboot的线上辅导班系统的开发与设计(毕业论文)

    一、用户管理功能 用户注册与登录 学生注册:学生可以通过手机号、邮箱、社交账号等方式注册,填写个人信息(如姓名、年龄、学校等)。 家长/监护人账户:支持家长/监护人注册并管理学生账户,查看学习进度和成绩。 教师账户:教师可以注册并设置个人资料,上传资质认证文件。 管理员账户:管理员负责整个系统的管理,包括用户管理、课程管理、平台设置等。 用户权限管理 角色权限:系统根据用户类型(学生、家长、教师、管理员)分配不同权限,确保信息安全。 家长监督:家长可以查看子女的学习进度、成绩和教师反馈,参与学习监督。 个人资料管理 用户可以在个人中心更新基本信息,设置个人头像、联系方式、密码等。 支持学籍信息的维护,例如学生的年级、班级、课程历史等。 二、课程管理功能 课程设置 课程创建与编辑:教师或管理员可以创建和编辑课程内容,上传课件、视频、文档等教学材料。 课程分类:根据学科、年级、难度等维度进行课程分类,方便学生浏览和选择。 课程排课:管理员可以设置课程的时间表、教学内容和授课教师,并调整上课时间和频率。 课程安排与通知 课程预约:学生可以在线选择并预约感兴趣的课程,系统根据学生的时

    英特尔2021-2024年网络连接性和IPU路线图

    内容概要:本文档介绍了英特尔2021年至2024年的网络连接性产品和智能处理单元(IPU)的战略和技术路线图。涵盖了从10GbE到200GbE的不同系列以太网适配器的特性、性能和发布时间。详细列出了各个产品的关键功能,如PCIe接口、安全特性、RDMA支持等。同时,介绍了IPU的发展计划,包括200G、400G和800G的不同代次产品的性能提升和新的功能特点。 适合人群:从事网络工程、数据中心管理、IT架构设计的专业技术人员。 使用场景及目标:本文档主要用于了解英特尔未来几年在以太网适配器和IPU领域的技术和产品规划,帮助企业在采购和部署网络设备时做出决策。同时,为研究人员提供最新技术发展趋势的参考。 其他说明:文档内容涉及的技术细节和时间表可能会有变动,请以英特尔官方发布的最新信息为准。

Global site tag (gtag.js) - Google Analytics