Ruby on Rails笔记


Ruby on Rails 笔记 V0.2 (笔记者:陈刚 http://www.chengang.com.cn ) 前 言 “读不如做,做不如写”,这一直是我学习技术所喜欢的方式。我是一个健忘的人,把知识写成文章 后,自己遗忘时查阅也就方便了很多,毕竟是自己写的东西一查就能记起大部份来。此文档是我学习 Ruby on Rails 技术的综合,有读书笔记、有心得、有自创教程、有一些问题的解决经验,统统分门别类集合 在了一起。 由于时间仓促,所以有些地方写得很简略,排版有些乱,错误肯定也不少。寄希望于以后不断更新此 文档,争取更完善起来。交流 Email:glchengang@163.com 参考书目:  Ruby on Rails 快速 Web 应用开发实战 编著:柳靖、曹璐、赵丹  RUBY 语言入门教程(Version 1.0)(开源文档) 编著:张开川 (kaichuan_zhang@126.com)  应用 Rails 进行敏捷 Web 开发 作者:Dave Thomas & Dvid Heinemeier Hansson 版本 日期 说明 V0.1 2007-5-12 主要是《Ruby on Rails快速Web应用开发实战》一书的笔记,以及《应用Rails进行敏捷WEB开发》的 部份笔记 V0.1 2007-5-28 补完了《应用Rails进行敏捷WEB开发》的笔记 ·1· Ruby on Rails 笔记  V0.1 (作者:陈刚) http://www.chengang.com.cn Rails 开发应用 第 1 章 Rails 起步 1.1 安装 Rails Rails 框架是基于 Ruby 语言的,就象 Struts 框架是基于 Java 语言的一样。所以先去下载 Ruby 安装包, http://rubyinstaller.rubyforge.org。安装 Ruby 很简单,略过。可以用“ruby -v”来检查 ruby 是否安装成 功,该命令显示的是当前 Ruby 的版本号。 接着安装 Rails,命令为“gem install rails -include-dependencies”,一定要注意保证网络通畅。 安装时间可能需要几分钟,如果不成功多试几次。以后可以用“gem update rails”命令更新 rails 为最新 版本。用“rails –v”来检查 rails 是否安装成功。   本 文档的运 行环境: Windows X P + ruby 1 .8.5 + rails 1 .2.3   1.2 Rails 的 HelloWorld 第一步  创建一 个 Rails 项目 创建 D:\work 目录,在 DOS 下进入此目录,然后执行命令“rails demo”,得到一个 Rails 项目, 其目录结构如下图所示 第二步  启动 Rails 内含的 WEB 服务器 在 DOS 下进入 D:\work\demo 目录,执行命令“ruby script/server”来启动 WEB 服务器(如下图 所示)。D:\work\demo\script\server 是一个脚本程序,可以用记事本打开它。 ·2· Ruby on Rails 笔记 V0.1 (作者:陈刚) http://www.chengang.com.cn 第三步  访问 WEB 页面 从上图右下角可以看到其 WEB 服务的端口号为 3000,访问地址为 http://localhost:3000,如果访问 成功,得到如下页面。 第四步  修改程 序 Rails 是以 MVC 来设计的,页面、控制器、模型的层次分得很清楚。 (1)我们先来创建一个控制器 文件名:hello_world_controller.rb,文件位于 D:\work\demo\app\controllers 目录。 class HelloWorldController < ApplicationController def say @word = "Hello World!!" who = "www.chengang.com.cn" puts @word + " by " + who end end 说明:  学过 Java 的看这个程序并不难。HelloWorldController 是类名,由于 Rails“约定优于配置”特 点,所有命名都是有规范的。控制器名称必须用 Controller 做后缀。  文件名则用小写,原类名的大写字母处用下划线分隔。  ApplicationController 是所有控制器的父类,<是继承符号,相当于 Java 里的 extends  def 定义了一个 say 方法,如果没有参数,方法后面的括号可以省略,当然加上括号也行。在控 制器中方法也被称为 Action(仅指公开方法,Rails 也象 Java 那样有私有方法)。  @word 定义了一个变量,没有象 Java 那种在前面加个 String 的类型指定。开头的@符号表示此 变量可以被前台页面访问。  who 是一个内部变量,前台页面无法访问。 (2)接着创建一个页面 文件名:say.rhtml,文件位于目录:D:\work\demo\app\views\hello_world。文件名就是控制器 里的方法名,目录 hello_world 是控制器名称的前缀。say.rhtml 的内容如下,写法很象 JSP,其中使用了 控制器中的@word 变量。 Rails Demo<title> </head> <body> <h1><%= @word %> </h1> </body> </html> 访问地址为:http://localhost:3000/hello_world/say,访问地址格式为:.../controller/action。 ·3· ·3· Ruby on Rails 笔记  V0.1 (作者:陈刚) http://www.chengang.com.cn (3)总结 控制器如何和页面联系在一起的呢?答案就在于 Rails“约定优于配置”的特点,由于名称之间是有 联系的,Rails 框架就能够很轻松的把各个零散的部份组装起来。 1.3 使用 RadRails 1.3.1 安装配置 RadRails RadRails 是一个 Eclipse 插件,用于开发 Rails 程序。下载:http://radrails.sourceforge.net,下载 文件名:radrails-0.7.1-win32.zip,解压之后就可以用了。下面主要是讲它的配置,这些配置都在首选项 窗口里(注:除了配置 ruby.exe 有扩展名以外, 其他设定都没有扩展名)。 Mongrel 是 WEB 服务器,这里可不设置。实际应用时,一般是用 Apache 做前端请求转发,后端用 mongrel 做集群,以实现大负荷访问。 ·4· Ruby on Rails 笔记 V0.1 (作者:陈刚) http://www.chengang.com.cn 1.3.2 创建 Rails 项目 用右键快捷菜单,在新建窗口选“Rails Project”项,在下一个窗口填入项目名 chensite。     单击“finish”按钮后,得到一个 Rails 项目,如下左图所示。为了解决中文乱码问题,这里提前把 chensite 项目的编码更改为 UTF-8,如下右图所示(右击项目名->选 properties 项)。   ·5· ·5· Ruby on Rails 笔记  V0.1 (作者:陈刚) http://www.chengang.com.cn 在 Eclipse 右下角的 Server 视图可以启动 WEB 服务器,如下图所示。 启动后的访问地址:http://localhost:3000/。在 Console 视图有 rails 的打印输出,如下图所示。 另外,通过 Generators 视图可以创建一个控制器 Controller,如下图所示。它和 DOS 下的 “ruby script/generate controller hello_world”命令效果一样。 在 Eclipse 中没有专门用于 rhtml 文件的新建项,以普通文件方式创建*.rhtml 即可。RHTML 编辑器内 含的代码提示(Content Assist)功能不强,只会提示一些基本的语法流程框架。另一个集成开发工具 NetBeans 的 6.0 版则有很强的 Rails 代码提示功能,但我习惯了用免费的 Eclipse,所以没有选用收费的 NetBeans。 1.4 使用数据库 Rails 项目必定要用到数据库,本文档使用的是 MySQL 数据库。 第一步 :数据库 准备 (1)建库。MySQL 中创建三个数据库,分别用于:开发、测试、产品。库名约定用项目名做前缀。SQL 脚本如下(另注:一般都把 SQL 脚本放在项目的 db 目录下): create database chensite_development; create database chensite_test; create database chensite_production; (2)建表。由于是开发阶段,先就只在 chensite_development 中建表。Rails 建议每个表都带一个 和业务无关的 ID 字段。建表的 SQL 脚本如下。 use chensite_development; # ·6· Ruby on Rails 笔记 V0.1 (作者:陈刚) http://www.chengang.com.cn # ------------- 用户--------------- # drop table if exists users; create table users ( id int not null auto_increment, username varchar(20) not null, password varchar(20) not null, email varchar(50) not null, createtime datetime not null, primary key (id) )ENGINE=InnoDB DEFAULT CHARSET=utf8; LOCK TABLES users WRITE; INSERT INTO users VALUES(1,'chen','111','glchengang@163.com','2007-04-11 23:59:59'); INSERT INTO users VALUES(2,'jack','222','jack@163.com','2007-04-12 23:59:59'); INSERT INTO users VALUES(3,'rose','333','rose@163.com','2007-04-13 23:59:59'); UNLOCK TABLES; 第二步 :在 Rails 项目中配置 数据库的 连接 修改 config\database.yml,给连接三个数据库的 root 用户输入密码,在输入密码时要注 意:"password:"和密码"123456"之间要有一个空格,并且密码之后不要有空格,否则无法启动 WEB 服务。 同样是为了解决中文乱码的问题,这里增加了一个 encoding 配置。 devel opm ent: adapter: mysql database: chensite_development encoding: utf8 username: root password: 123456 host: localhost test: adapter: mysql database: chensite_test encoding: utf8 username: root password: 123456 host: localhost pr od uc tio n: adapter: mysql database: chensite_production encoding: utf8 username: root password: 123456 host: localhost 第三步 :创建一 个能够显 示数据库 数据的页 面。 (1)创建数据模型:app\models\user.rb class User < ActiveRecord::Base end  Rails 命名约定:数据库名为小写复数 users,文件名为小写单数 user.rb,类名为大写单数 User。 另外,如果数据库名为 site_users,则文件名为 site_user,类名为 SiteUser。  模型类中不必定义属性,它会自动以数据库字段为属性。 (2)创建视图:views\homepage\index.rhtml,显示出 user 表所有记录的 id 和 name 值 <html> <body> ·7· ·7· Ruby on Rails 笔记  V0.1 (作者:陈刚) http://www.chengang.com.cn <h1>ChenGang's Site</h1> <% for o in @users %> ID:<%= o.id %>,而用户名是<%= o.username %><br/> <% end %> </body> </html> 视图中用到的@users 变量来自于我们自己创建的如下控制器: app\controllers\homepage_controller.rb。Rails 中控制器中的变量可以在视图中使用。 class HomepageController < ApplicationController def index @users = User.find(:all) end end  Homepage 和视图的目录名 homepage 相关  index 方法和视图的文件名 index.rhtml 相关  用 index 可以省略访问地址中的 action(action 默认为 index) 最后启动 Web 服务器后访问:http://localhost:3000/homepage,效果图如下: 1.5 用 scaffold 来实现 CRUD 1.5.1 简单的 scaffold 应用 CRUD 是写(Create)读(Read)修(Update)删(Destory)的意思。scaffold 是 rails 内含的增 删改模板,用它可以不用写什么代码就能得到对一个数据模型的增删改页面。在 homepage_controller.rb 中增加如下一句“scaffold :user” class HomepageController < ApplicationController scaffold :us er def index @users = User.find(:all) end end 访问:http://localhost:3000/homepage/new,得到下图 ·8· Ruby on Rails 笔记 V0.1 (作者:陈刚) http://www.chengang.com.cn 填入信息,单击 Create 按钮之后,得到如下图。 仅仅对 User 模型做了一个 scaffold 标注,就得到了对 User 数据的增删改页面。 1.5.2 改进 scaffold 应用 很多时候我们需要的增删改页面要复杂得多,往往涉及到多个数据模型。比如,现在有一个 article 表 (文章),它和 user 是一对多的关系(一个人可以发表多篇文章)。 1.SQL 脚本 article 的 SQL 脚本如下。它应该包含有一个 user_id 字段,做为和 User 表联系起来的外键,此字段是 有命名约定的,格式为:“外联的表名_id”。 create table articles ( id int not null auto_increment, title varchar(30) not null, content text(3000) not null, createtime datetime not null, user_id int not null, primary key (id) )ENGINE=InnoDB DEFAULT CHARSET=utf8; 2.数据 模型 为了反映两表的一对多关系,还需要修改一下数据模型,各加上 has_many 和 belongs_to 标注。 cla ss User < ActiveRecord::Base has_many :a rt ic le end cla ss Article < ActiveRecord::Base belongs_to :us er end 3.控制 器 为了方便管理,我们创建两控制器,如下。 cla ss UserController < ApplicationController scaffold :user end cla ss ArticleController < ApplicationController scaffold :ar ti cl e end 访问:http://localhost:3000/article/new ,显示的是 article 的新增记录页面,从页面上看 user_id 字段没有显示在上面。我们希望在页面上新增一个选择文章所属用户的下拉框。这时就需要手动编 程了。再次改写控制器 ArticleController 如下: class ArticleController < ApplicationController scaffold :article def create ·9· ·9· Ruby on Rails 笔记  V0.1 (作者:陈刚) http://www.chengang.com.cn @article = Article.new(@params['article']) #用页面传入数据创建一个文章对象 @article.createtime = Date.today #把日期字段设为今天 if @article.save redirect_to :action =>'list' else render_action 'new' end end def new @articles = Article.new @users = User.find_all #取出所有用户,前台页面将用到,把用户名列在下拉框中 end # 如果改写 list 方法,默认 scaffold 的 list 页面会失效,这就需要新写一个 list.rhtml,否则会报找不到 list.rhtml 的错 误 # def list # @articles = Article.find_all # end def edit @article = Article.find(@params["id"]) @users = User.find_all end end 4.页面 接下来就创建前台页面了,需要在默认的页面里增加一个下拉框把用户名列出来。取巧的办法是用浏 览器查看默认页面的源代码,然后在其上做修改。不过有些 Rails 表单标签已经转化成了 HTML 代码,你可 以手工再改回 Rails 标签,当然不改也不会出错。 app\views\article\new.rhtml 中新增加的下拉框的主要代码 <select name="article[user_id]"> <% @users.each do |o| %> <option value="<%= o.id %>"><%=o.username %></option> <% end %> </select> edit.rhtml 中新增加的下拉框的主要代码如下: <select name="article[user_id]"> <% @users.each do |o| %> <option value="<%= o.id %>" <%= 'selected' if o.id == @article.user_id %>><%=o.username %></option> <% end %> </select> 1.6 中文乱码问题 前面我们没有出现中文乱码问题,主要是我们在建表、配置项目等时已经考虑到中文乱码问题了。这 一节再总结一下,我们是怎么解决中文乱码问题的。 首先我的建议是统一采用 UTF-8 编码,它的灵活最大。也有些人采用 GB2312 或 GBK,当然这两种编 码对中文应用来说足够了。但在这个逐渐变平的世界里,你的网站很可能就要推向越南、日本等市场,这 时 GB2312、GBK 就不管用了,所以我们在一开始就向世界通行的标准 UTF-8 靠拢。 中文编码统一的关键点如下:  设置 MySQL 环境及表的编码是 utf8 ·10· Ruby on Rails 笔记 V0.1 (作者:陈刚) http://www.chengang.com.cn  database.yml 里增加一行 encoding: utf8  将*.rhtml 文件编码设为 UTF-8 (1)数据库 MySQL 的环境配置为 UTF8。它可以在初安装时选择,以后也可以用“MySQL Server Instance Config Wizard”来重新设置,其设置界面如下 (2)建表的时候选择 UTF8。在以前的建表语句结尾都有如下一句: )ENGINE=InnoDB DEFAULT CHARSET=utf8; (3)在 config/database.yml 中加入一句编码设置,如下所示。 …… adapter: mysql database: chensite_development encoding: utf8 username: root …… (4)在完成以上设置之后,从数据库读取的数据显示正常了,但*.rhtml 里的原中文字符却显示成 了乱码,将*.rhtml 用记事本重新保存为 utf-8 格式可以解决此问题。我们这里是用 RadRails,它的解决方 案是:右击项目,在它的属性页的 info 项的 text file encoding 改为 UTF-8,如下图所示。 这时用 RadRails 的编辑器打开*.rhtml 文件,其中的中文显示成乱码。解决方案是:用 Windows 的记 事本打开文件,中文显示还是正常的,然后我们再复制粘帖到 RadRails 环境的编辑器中。 后注: (1)有的人说要修改 application.rb,在 before_filter 加入字符过滤代码。我以前也试过,这个方 ·11· ·11· Ruby on Rails 笔记  V0.1 (作者:陈刚) http://www.chengang.com.cn 法是可行的。但我觉得还是修改 database.yml 来得简单一些。 (2)有的人说要同时在*.rhtml 里加上如下编码设置。我发现这一句可加可不加,对页面编码显示没 有任何影响。 <meta http-equiv="Content-type" content="text/html; charset=utf-8" /> (3)有的人说修改 environment.rb 加入两行 $KCODE = 'u' 和 require 'jcode' 。这里似乎没有 必要。也许这是老版 Rails 的解决方法。 (4)有的人说在建表的时候不能用 InnoDB,但本文都是用 InnoDB 的,没有问题。 …… )ENGINE=InnoDB DEFAULT CHARSET=utf8; 1.7 其他知识点 1.7.1 link_to(链接)  link_to "编辑", :action=>"edit" 此句创建一个指向 edit 这个 Action 的 HTML 链接,链接显示字符为“编辑”  link_to 除了 action,还可以定义 controller,以及 action 的参数 link_to "About", :controller=>"showpage", :action=>"about", :id=>11  此句生成的 url 为 http://.../showpage/about/11  如果把上一个 link_to 语句的 id 部份改写为:name=>"glchengang" ,则生成的 URL 为 http://.../showpage/about?name="glchengang"。和前一个 url 比较得知,id 是默认参 数,不显示在浏览器的 URL 地址中。 link_to “带 html 选项的示例”, {:controller=>'article', :action => 'show', :id => o.id}, {:target=>'_blank'} #html 选项 1.7.2 字符串 puts 打印输入到 console pp 打印输出 p 打印输出 h() 方法用于打印输出包含%<>等字符。此方法的全称 html_escape() sanitize() 可以过滤掉字符串中一些危险的 HTML 元素。 debug(params) 常用于打印传入参数 pluralize(2,"person") 得到英文复数:people,再如 category->categories highlight('my name is chengang', 'chen') chen 在参数 1 中被高亮显示 更多字符串格式方法,参见《应》p348 1.7.3 本地命令调用 render(:text =>"<pre>"+ CGI::escapeHTML('ps -a') +"</pre>") 只能在 UNIX 下使用。 ·12· Ruby on Rails 笔记 V0.1 (作者:陈刚) http://www.chengang.com.cn 1.8 常用 Ruby 命令  rails demo 创建一个 Rails 项目,项目名 demo  ruby script/generate scaffold Product Admin 针对 Product 表产生一整套的网页程序(增删改),Admin 是控制器名  ruby script/generate controller Store index 创建一个名为 Store 的控制器,并含有一个 index() Action 方法。  ruby script/server 启动 WEB 服务,访问 http://localhost:3000/admin 第 2 章 数据模型 Active Record 2.1 基础 cla ss User < ActiveRecord::Base set_table_name "table1" 指定非默认的表名 set_primary_key "name" 指定非默认主键。以后 name 字段就用成 id,如 o.id="chengang" end 2.2 创建记录 Rails 创建记录很灵活,存在有各种方式,如下代码片断所示: student1 = Student.new student1.stu_num = "0000000001" student1.name = "Mary" student1.department = "Computer Science" student1.save Student.new do |stu| stu.stu_num = "0000000002" stu.name = "John" stu.department = "Computer Science" stu.save end student3 = Student.new( :stu_num => "0000000003", :name => "Helen", :department => "Computer Science" ) ·13· ·13· Ruby on Rails 笔记  V0.1 (作者:陈刚) http://www.chengang.com.cn student3.save student4 = Student.create( :stu_num => "0000000004", :name => "Robert", :department => "Computer Science" ) students = Student.create( [ { :stu_num => "0000000005", :name => "Jane", :department => "Computer Science" }, { :stu_num => "0000000006", :name => "Tom", :department => "Application Technology" } ]) 2.3 读取记录 Student.find(3) #查找 id=3 的记录 Student.find(:first) #取出第一记录 Student.find_by_sql("select * from students where stu_num = '0000000005'")  #直接使用 SQL Student.find_by_name("chengang") 根据姓名查询 Student.find_by_name_and_sex("chengang","男")  根据姓名和性别查询 Student.find(:all, #取出所有记录 :conditions => "name = 'John' and department like '%Computer%'", #带条件查询 :order => "id DESC", #根据 id 倒序 :limit => 3, #只取出 3 条记录 :offset =>2)  #偏移量:去掉结果集中的前 2 条记录 students = Student.find_by_sql("select name from students") 得到的对象只包含 name 属性 p students[0].attributes   显示:{“name”=>”chengang”} p students[0].attribute_names 显示:[“name”] p students[0].attribute_present?("sex") 是否存在 sex 属性,显示:false Student.count  记录数 Student.count(["name = ?", "Mary"]) 符合条件的记录数 Student.count_by_sql("select count(*) from students")  根据 SQL 求出记录数 Student.count("age > 21") Student.average(:age) Student.minimum(:age) Student.maximum(:age) Student.average(:age, :conditions => ["name like ?", '%T%']) Donation.sum(:amount, :group => :created_at) Donation.sum(:amount, :group => 'YEAR(created_at)') Rating.average(:value, :group => :post).collect{|post, rating| [post.title, rating]} Rating.average(:value, :group => :post)[Post.find(2)] Student.find_by_name("Adam").ratings.average(:value) 数据模型 Active Record 还有几个属性方法:  content_columns 得到所有字段 ·14· Ruby on Rails 笔记 V0.1 (作者:陈刚) http://www.chengang.com.cn  content_columns.name 字段名 2.4 更新记录 student_1 = Student.find(5) student_1.department = "Biology" student_1.save! student_2 = Student.find(5) student_2.update_attribute(:department, "Computer Science") student_3 = Student.find(5) student_3.update_attributes(:name => "Bob", :stu_num => "0000000010") Student.update_all("department = 'None'", "department like '%Computer%'") 2.5 删除记录 Student.delete(4)  删除 id=4 的记录 Student.delete([5,6])  删除 id 为 5,6 的记录 student = Student.find(:first) 删除第一条记录 student.destroy student 对象 destroy 方法之后,依然可用 Student.destroy_all("id > 2")  删除 id>2 的记录 delete 和 destroy 的区别:前者删除记录不会调用 ActiveRecord 中的回收函数和验证函数,而后 者会去调用。建议用 destroy。 2.6 聚集操作 #聚集类 class Name attr_reader :first, :last   #定义两个属性 def initialize(first, last) #构造函数 @first = first @last = last end def to_s #改写 to_s [ @first, @last ].compact.join(" ") end ·15· ·15· Ruby on Rails 笔记  V0.1 (作者:陈刚) http://www.chengang.com.cn end class Account < ActiveRecord::Base #定义一个聚集字段 composed_of :name, #新字段名 :class_name => Name, #类名 :mapping => #定义表字段和聚集类属性的关联关系 [ #Table Columns, RoR code [:first_name, :first], [:last_name, :last] ] end Account.create( [ { :first_name => "James", :last_name => "Easton", :saving => 4200.00 }, { :first_name => "Zhang", :last_name => "San", :saving => 3200.00 }, { :first_name => "Li", :last_name => "Si", :saving => 1200.23 } ]) name = Name.new("Peter", "Krogh") Account.create(:saving => 324.87, :name => name) account = Account.find(:first) account.name account.name.first account.name.last 2.7 事务处理 User.transaction do #user 数据对象的操作 end ·16· Ruby on Rails 笔记 V0.1 (作者:陈刚) http://www.chengang.com.cn 2.8 数据表间的关联 2.8.1 几种关联 1.一对 一 学生(students)和成绩(grades)是一对一的关系。一般来说,学生是主表,成绩是附表,并且成绩表 有一个和学生表 id 对应的 student_id 字段。 则学生表模型中用:has_one :grade 成绩表模型中用: belongs_to :student 以后可以这样使用:student1.grade.id 2.一对 多 如果学生有多课成绩,则两表成了一对多关系。 则学生表模型中用:has_many :grades 成绩表模型中用: belongs_to :student 3.多对 多 则学生表模型中用:has_and_belongs_to_many :grades 成绩表模型中用: has_and_belongs_to_many :students 还要创建一个中间数据表:grades_students(按首写字母顺序决定两个单词先后) 2.8.2 关联中的更多设置 Rails 中命名约定优于配置,如果我们不得已违反了命名约定,则 Rails 也提供有设置参数。以下即是 关联方面的设置参数。 1.belongs_to 的属性 belongs_to : student, :class_name => " Student ", #指定父类 :foreign_key => "student_id",  #指定关联外键 :conditions => "id is not null",  #指定约束条件 2.has_one 的属性 has_one 除了拥有 belongs_to 的一样属性,自己还有两个属性: :dependent => true  父表记录删除,子表相应记录也自动删除。 :order =>"id DESC" 设定返回记录的排序顺序。用 has_one 的可能是子表,这时本表和父表是多对 一关系,当查父表时,子类就可以根据 order 来排序。 3.has_many 的属性 has_many 除了拥有 has_one 的一样属性,自己还有三个属性: :exclusively_dependent => true 说明:作用和 dependent 同样,但删除速度更快。两属性不能同时使用 :finder_sql => "select i.* from categories c, items i where i.category_id = c.id " 说明:指定返回的数据集 :counter_sql => "select count(*) from items" ·17· ·17· Ruby on Rails 笔记  V0.1 (作者:陈刚) http://www.chengang.com.cn 说明:重写计数器 4.has_and_belongs_to_many 的属性 has_and_belongs_to_many 除了有以上提到的属性外, class User < ActiveRecord::Base has_and_belongs_to_many :items def read_item(item) items.push_with_attributes(item, :read_time => Time.now) end end pp item = Item.find(:first) pp user = User.find(:first) user.read_item(item) pp user.items pp item.users pp user.items.find(:first).read_time pp item.users.find(:first).read_time 2.9 列表 有些时候显示在页面上的数据是有顺序的,也即列表。 class Parent < ActiveRecord::Base has_many :children, :order => :position end class Child < ActiveRecord::Base belongs_to :parent acts_as_list :scope => :parent_id # scope 设定了每个 parent_id 对应一个单独的列表,否则所有记录都 在一个大表中。 end def show_children(parent) #打印数据记录 puts parent.children.map {|child| child.info}.join(", ") end parent = Parent.new %w{first secord third fourth fifth}.each do |info| parent.children.create(:info => info) end parent.save! show_children(parent) three = parent.children[2] three.move_to_top() #移动到最前面。 #其他:move_lower()下移一位;move_higher()上移一位;move_to_bottom()最末 parent.reload   #重新装入记录。否则内存中的记录位置还是不会改变 show_children(parent) pp three.higher_item.info  # higher_item 当前元素的前一个 pp three.lower_item.info three.destroy  删除记录 parent.reload ·18· Ruby on Rails 笔记 V0.1 (作者:陈刚) http://www.chengang.com.cn show_children(parent) 2.10 模型的验证 在模型类用一些 validates_*属性,可以定义一些字段的验证特性。不需要写验证代码,Rails 框架自 动进行验证。  validates_presence_of :title, :desc, :image_url 必须不为空  validates_numericality_of :price  必须是数字  validates_uniqueness_of :title  必须唯一  validates_format_of :image_url, 文件名必须是图片文件的扩展名 :with => %r{^http:.+\.(gif|jpg|png)$}i, :message => "must be a URL for a GIF, JPG, or PNG image"  模型保存到数据库之前会调用 validate 方法,例: def validate errors.add(:price, "should be positive") unless price.nil? || price > 0.0 end 2.* 一些问题 今天还碰到一个问题,我把表 titles 改名为 modules 后,模型文件名为 module.rb,其他部份也做 了修改。运行后出了错,出错信息是 Module.class 没有 find 方法(我在 action 调用 find 方法)。如下: NoMethodError in SiteController#index undefined method `find' for Module:Class RAILS_ROOT: ./script/../config/.. Application Trace | Framework Trace | Full Trace #{RAILS_ROOT}/app/controllers/site_controller.rb:21:in `header' #{RAILS_ROOT}/app/controllers/site_controller.rb:5:in `index' -e:4:in `load' -e:4 多方尝试后发现数据库模型类不允许起名为 module(这应该是 rails 内部的一个类,或者是 rails 是 一个关键字),最后的解决办法是加一个下划线后缀,然后用 set_table_name 指定映射的表名。文件名: module_.rb class Module_ < ActiveRecord::Base set_table_name "modules" end ·19· ·19· Ruby on Rails 笔记  V0.1 (作者:陈刚) http://www.chengang.com.cn 如果出现 SessionRestoreError 错误,则检查 application.rb 是否做了如下的模型声明。因为对于动 态语句,session 把序列化的类取出时,否则是无法知道对象类型的,除非声明一下。 class ApplicationController < ActionController::Base model :cart model :line_item end 第 3 章 控制器 controller 3.1 URL 访问 3.1.1 定制 URL 配置文件地址:config\routes.rb 例 1:map.connect 'aaa/:action/:id',:controller=>'site' 说明:地址 http://localhost:3000/aaa/index/1,中的 aaa 实际指向的是 site 控制器 ·20· Ruby on Rails 笔记 V0.1 (作者:陈刚) http://www.chengang.com.cn 例 2:map.connect ':action/:id',:controller=>'site' 说明:地址 http://localhost:3000/index/1 虽然没有指定控制器,但实际指向 site 控制器。 例 3:map.connect ':id',:controller=>'site',:action=>'index' 说明:地址 http://localhost:3000/1,将实际指向 site 控制器的 index Action 另外“:requirements => {:id => /^[0-9]+$/}”这样的设置可以将 id 部份的值限制为数字 除了:controller、:action 参数还有:year、:month、:day 表示年月日的参数 用 url_for 方法可以生成 URL,但它必须符合 map.connect 中的定义。 url_for(:controller=>”site”,:action=>”show”, :id=>111) 3.1.2 有名路由 前面用 map.connect 定制 URL 的属于匿名路由,还可以设置有名路由,以方便管理。有名路由的方 法和匿名路由基本一样,就是把 map.connect 改名 map.anyname(anyname 指任何名称)。而 url_for 方法用在有名路由就需要变成 anyname_url 3.2 Action 方法 3.2.1.hide_action 声明 action 方法为 private 或 protected 型 例:hide_action :some_action 则 some_action 变成 private 型 3.2.2.red irect _to 此方法用于 action 方法间的跳转 例: redirect_to :action=>"edit", :id=>7 redirect_to "http://www.chengang.com.cn" redirect_to "/images/1.jpg" #跳转到../image/1.jpg redirect_to :backk #返回上一次访问的页面 3.2.3.rend er 显示输出给用户(显示在页面上) 例: ·21· ·21· Ruby on Rails 笔记  V0.1 (作者:陈刚) http://www.chengang.com.cn render :text=>'HelloWorld!!' render(:action=>:index) 调用父类的 index()方法并显示其结果。《应 》p340 说它不会调用 index 方 法,而是直接显示模板。 render(:template=>"test/index") 显示 index.rhtml 模板 render(:file=>”F:/index.rhtml”) 显示指定文件 render(:file=>”test/index”, :user_full_path=>true) 显示文件,参数 2 表示用相对路径 render(:nothing=>true) 显示一个空页面 render :inline=>”<%=’Hello’ + str %>”, :locals=>{:str=>”World”}, :type=>”rhtml” 显示 一个动态模板。参数 1 是模板代码,参数 2 是模板可用的变量值,参数 2 是模板类型(or rxml) 注:“第 4.2.2 节 布局模板”及“第 4.2.3 节 局部模板”还有更多关于 render 的应用。 3.2.4.rend er_to_string 和 render 用法同,但本方法返回值是字符串类型。 3.2.5.send_d ata 向客户端发送二进制数据流。 例 img= Photo.find(:first) send-data(img, :type=>”image/jpeg”, :disposition=>”inline”) 参数 1 可以指定一个文件名,参数 2 指定所传送的数据类型,参数 2 为 inline 显示数据或 attachment 下载并保存数据。 3.2.6.send_ file 本方法和 send_data 类似,但它更适合大数据量的传输。除了有 send_data 的三个参数外,本方法 还有两个参数,:streaming=>true(边读边传,false 为读完文件到服务器内存再传给客户端),: buffer_size=>4096 数据缓冲区大小(一次传输的数据量,4096 字节是默认值) 3.2.7.fl ash――action 间的通信器 action 之间的变量是不能互访的,带@前缀的也不行。就是 flash 的有效期也只能在下一个 Request 之间有效,再过一个 Request 就失效了。实例变量是代替不了 flash 的,因为 IE 无状态的特性,在下一个 请求中,上一个请求的实例变量已失效。 flash[:myname]= ‘chengang’ 存值,取值是 puts flash[:myname] flash.keep(:myname) 可以保证 myname 的值不被修改。即使该值来自上一个 action,用 keep 之后,它就可以再传到下一个 action。 flash.now[:myname] = “chengang” now 和 keep 相反,它指定的值只在当前 action 有效 3.3 开发环境预设对象 这些对象是可以直接使用的 ·22· Ruby on Rails 笔记 V0.1 (作者:陈刚) http://www.chengang.com.cn 3.3.1 cookies 对象 封装了客户端的 Cookie 信息 cookies[:name] = “chengang” cookies[:current_time] = Time.now.to_s cookies[:key1] = {:value=>”four”, :expires=>10.days.from_now} 此 10 天后过期 3.3.2 params 对象 这个应该很熟悉了,它是一个哈希结构的对象,用于封装传递到 Action 方法的参数。可以用 params[:id]或 params[‘id’]取值。 3.3. 3 request 对象 此对象对应于用户的各种请求。 request.get?.to_s 得到一个布尔字符 true 或 false。判断是否是一个 GET 请求 request.post?.to_s  是否是一个 POST 请求 request.host() 返回主机地址 request.protocol 返回协议类型,如 http:// request.env[“REQUEST_URI”] 返回访问的 URL,如:http://localhost:3000/site/index/1 request.env[“HTTP_ACCEPT_LANGUAGE”] 返回示例:zh-cn 3.3. 4 session 对象 没有给出 session 的应用示例,暂略 3.4 零散知识 model :model1,:model2,:model3  model 属性在控制器类中可以指定非默认模型类,并指定这 些模型可序列化。 helper :date_formate helper 声明可以让控制器使用非默认的帮助模块 date_formate_helper.rb 当异常没有被任何程序捕捉,最后总会转到 ApplicationController 的 rescue_action_in_public() 方法 第 4 章 界面组件 Action View ·23· ·23· Ruby on Rails 笔记  V0.1 (作者:陈刚) http://www.chengang.com.cn 4.1 rxml 模板 《书》中的实例无法运行通过。 4.2 rhtml 模板 4.2.1 帮助模块 helper abc_controller 对应帮助模块 abc_helper,application_helper.rb 是大家都可以访问的帮助模块。 在控制器里加入“helper :test”可以访问另一个帮助模块 test_helper 4.2.2 布局模板 布局模板一般位于 app\views\layouts 目录,如果当前控制器为 StoreController,则它就会去找布 局模板 store_layout.rhtml(还是 store.rhtml?)。没有定义布局模板的控制器会使用 application.rhtml 这个布局模板(如果它存在的话)。 布局模板框架示例如下,其中变量的位置将嵌入 action 对应的 rhtml 输出。 …… <%= @content_for_layout %> …… 控制器中可以用 layout 声明使用非默认的布局模板,如以下语句声明控制器使用 standard.rhtml 做 为布局模板,并且把 rss、atom 两 action 排除在使用布局模板之外。 layout “standard”, :except=>[:rss,:atom]  还能用 only 来指定使用布局模板的 action  如果把 standard 变为 nil,则该控制器禁用布局模板。 使用以下 render 语句,action 也可以选择自己所使用的布局模板 render(:layout =>false) 不使用布局模板 render(:layout=>”layouts/simple”) 使用 simple_layout.rhtml 布局模板 4.2.3 局部模板 1.基础 示例 局部模板名称必须以下划线为前缀,如:_title.rhtml。在 rhtml 中调用局部模板示例,如下有两个属 于 partial 控制器的局部模板。 _title.rhtml <p>书名:<%= @first_item.name %></p> <p>书名:<%= title.name %></p> 在主模板可以这样调用 <%= render(:partial => "title", :object => @first_item) %>  名称前的下划线省略,调用当前目录下的局部模板_title.rhtml。参数 2 是传入值,在页面里用局 部模板的名称做为本地变量名 title(没有@前缀)来引用参数值。  如果控制器内有一个和局部模板名相同的实例变量,那么就可以在局部模板中就可以直接使用此 实例实量,因此能省略 object 参数。 ·24· Ruby on Rails 笔记 V0.1 (作者:陈刚) http://www.chengang.com.cn  一般把局部模板放在一个目录中,以实现各控制器的共享。如:partial => "shared/name",就会去找 app/views/shared/_name.rhtml 2.用 locals 传更多本地 变量 render 还有一个:locals 属性,它能把一个哈希表传给局部模板,而哈希表的键名则变成本地变量名。 例::locals => {:name =>”chengang”, :job=>”programer” } 注:实例变量局部模板可以直接使用。 3.用 collection 来使局部模 板输出集 合 :collection 属性指向一个数组,其的示例如下 <%= render(:partial => "name", :collection => %w{ chen tom jack }) , :spacer_template => “spacer”%> _name.rhtml 如下,变量名就是局部模板名: <p>第<%= name_counter %>位作者是<%= name %></p> _spacer.rhtml 如下 <hr /> 最后的效果是一组人名列表,其间以换行分隔。也就是说 collection 有着循环输出的效果。 4.2.4 组件化 1.基础 示例 有时我们不仅想要共享视图(rhtml),还要共享视图背后的逻辑(action),这时可以使用 render_component 方法。 <%= render_component(:controller =>:store, :action =>:cart_summary) %> 这时会执行 StoreController.cart_summary 方法,并将结果嵌入到所在模板中。这里要注意,如果 cart_summary 也使用了 render_component 语句所在的模板,则会形成无穷递归调用。这时以下语句 可能会用到。 layout “standard”, :except=>[:cart_summary] render(:layout =>false) 不使用布局模板 render_component_as_string 将渲染结果以字符串返回,而不是直接送给浏览器。这样,发起调 用的 action 就可以执行自己的渲染逻辑了。 2.把组 件放到 components 目录的示例 <%= render_component(:controller =>’ sidebar/store’, :action =>’cart_summary’) %> 控制器 components/sidebar/store_controller class Sidebar::StoreController < ActionController::Base #Sidebar 前缀有了自己的命名空间,避免了名字冲突 uses_component_template_root #有这一句 Rails 才会去 components 目录(而不是 app/views)去找模 板文件 …… def car_summary #对应的模板在 components/sidebar/store/ cart_summary.rhtml …… render(:layout=>false) end end , ·25· ·25· Ruby on Rails 笔记  V0.1 (作者:陈刚) http://www.chengang.com.cn 4.3 表单实现 这是一个用于搜索功能的表单: <%= form_tag(:action => :search_article, :class =>"none", :multipart => true) %> <%= text_field(:search, :keyword, :size=>"11", :maxsize=>"100") %> <%= hidden_field(:search, :user_id, :value=>@current_user.id) %> <input type="image" src="/images/go.gif" value="submit" class="submit" /> <%= end_form_tag %>  class="none"是设定 CSS 的 class 属性  multipart=>ture 表示表单可以提交 multipart 数据(如上传文件) 在 controller 的增加如下方法,用以处理表单传过来的数据。 def search_article if request.post? user_id=params[:s ear ch ][:user _i d] key=params[:s ear ch ][:key wo rd ] end end 4.4 分页实现 Rails 的分页实在是简单到了极点。一个文章列表的分页是这样实现的。 1.先在 Action 使用 paginate 方法,如下。其中得到的数据记录会在@articles 变量里,页的信息在 @article_pages 变量里。paginate 方法的第一参数是数据表名,order_by 根据 id 排倒序,conditions 是查询条件,per_page 是每页三条记录。 @article_pages,@articles = paginate(:articles, :order_by => 'id DESC', :conditions => "user_id=" + user_id, :per_page => 3) 2.接着在页面里就可以把@articles 变量里的记录显示出来,而在*.rhtml 文件里显示分页的那一栏 的代码为 <%= pagination_links(@article_pages)%> 以上实现的缺点是分页栏的式样固定,只列出了页码。而上页、下页这样的翻页按钮,参考如下实现。 <%= if @article_pages.current.previous link_to("Previous page", { :page => @article_pages.current.previous }) end %> <%= if @article_pages.current.next link_to("Next page", { :page => @article_pages.current.next }) end %> 4.5 缓存 1.模板 的 html 片段缓 存 ·26· Ruby on Rails 笔记 V0.1 (作者:陈刚) http://www.chengang.com.cn <% cache do %> ……需要被缓存的 html 语句 <% end %> 注:以产品模式启动 WEBrick(启动时加–e production 参数),开发模式下缓存无效。 2.在控 制器中对 缓存的判 断 def list …… unless read_fragment(:action=>’list’) #判断当前 action(list)是否缓存了 html 片段,如没有则…… …… end end 3.缓存 片段的失 效 如果数据库数据有了更新,就应该使缓存失效。以下语句可以使 list 对应模板内的缓存片段失效,不 过这要求页面里只有一个缓存片段才有效。 expire_fragment(:controller=>’blog’,:action=>’list’) 它还能使用正则表达式。 expire_fragment(%r{/blog2/list.*}) 4.定义 片段名: <% cache(:action=>’list’, :part=>’articles’) do %> ……需要被缓存的 html 语句 <% end %> 这时使它失效的语句如下: expire_fragment(:action=>’list’, :part=>’articles’) 4.6 其他知识点 base_path 可以得到当前*.rhtml 的目录路径 第一个链接效果相当于以前的“http://..../show.jsp?id=11”,第二个链接会弹出提示框。 <%= link_to 'Show', :action => 'show', :id => product %><br/> <%= link_to 'Destroy', { :action => 'destroy', :id => product }, :confirm => "Are you sure?" %> truncate(product.desciption, 80) 显示产品描述字段的摘要(80 个字符) product.date_available.strftime("%y-%m-%d") 日期字段的格式化显示 sprintf("%0.2f", product.price) 数值的格式化显示, number_to_currency 方法也有类似功能 public/stylesheets 目录保存了网页所用的 CSS 式样表。用 ruby script/generate scaffold ....生成框 架代码的网页自动使用 scaffold.css ·27· ·27· Ruby on Rails 笔记  V0.1 (作者:陈刚) http://www.chengang.com.cn 第 5 章 RoR 的 Ajax 在页面中加入如下一句,即可使用 RoR 中的 Ajax 库(prototype.js) <%= javascript_include_tag "prototype" %> 5.1 link_to_remote link_to_remote 是一种将某页面内容添加到主页面的技术 <%= link_to_remote("获得页面(不重复)", :update => "page_space_1", #div 的 id 值 :url => {:action => :get_page}) %> <div id="page_space_1"></div> <%= link_to_remote("获得页面(重复)", :update => "page_space_2", :url => {:action => :get_page}, :position => "after") %> #after 表示不断累加在后面, top 表示累加在前面 <div id="page_space_2"></div> 上面调用到的 get_page 方法在 controller 的代码如下,它是调用了一个局部页面。 def get_page render(:partial => "link_ajax_tag/ajax_content") end 传值的示例(link_to_remote 是通过 POST 来传参数值的) <%= link_to_remote("ss", :update => "primaryContent", #div 的 id 值 :url => {:action => 'search_article',:site_id=>@site.id,:keyword=>"ss"}) %> 它的效果与 http:/.../search_article?site_id=***&keyword=ss 类似,在页面中用 parms[:site_id]读 取传入值。 5.2 form_remote_tag 作用和 link_to_remote 类似,不过此标签是用于 form 表单的,用来代替表单头的 form_tag 标签。 <%= form_remote_tag(:update => "primaryContent", #div 的 id 值 :url => {:action =>'search_article'}) %> ·28· Ruby on Rails 笔记 V0.1 (作者:陈刚) http://www.chengang.com.cn 5.3 observe_field 用户改变表单中的某一项的值,改变动作被观察器监听到,然后进行相应处理(比如象前面 link_to_remote 示例一样在表单中增加一项)。observe_field 就是实现其中的观察器。 <%= radio_button(:time, :now, "show") %>显示时间<br/> <%= observe_field(:time_now_show, #对应 radio_button 的 ID :frequency => 1,   #监听周期:1 秒 :update => :time_show_place,  #更新的页面组件 :url => {:action => :time_show}) %> #调用的控制器 action 方法 <div id="time_show_place"></div> 如果 observe_field 用于文本框,则在 action 方法中,request.raw_post 可以得到文本框的值。如 果用/controller/time_show?20050701,则 request.query_string 可以得到传入值。 5.4 periodically_call_remote 此标签用于周期性触发交互操作(例如在网页显示一个不断刷新的时钟)。 <%= periodically_call_remote(:update => :time_show_roll, #更新的页面组件      :url => {:action => :time_show}, #调用的控制器 action 方法 :frequency => 1) %> #触发周期:1 秒 <div id="time_show_roll"></div> 5.5 可视化效果 1.渐失 /渐显 可视化效果需要 effects.js 支持,此库依赖 prototype.js,以下为示例 <html> <head><%= javascript_include_tag "prototype","effects" %></head> <body> <h1 id="id1">AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA</h1> <%=link_to_remote("Fade",:complete=>"new Effect.Fade('id1')",:url=>{:action=>"abc"})%> <%=link_to_remote("Highlight",:complete=>"new Effect.Highlight('id1')",:url=>{:action=>"abc"})%> <%=link_to_remote("Puff",:complete=>"new Effect.Puff('id1')",:url=>{:action=>"abc"})%> <%=link_to_remote("Squish",:complete=>"new Effect.Squish('id1')",:url=>{:action=>"abc"})%> </body> </html> 控制器里的 action def abc render(:nothing=>true) end 说明:  Fade 通过改变透时度让元素消失。Appear 则相反  Highlight 元素背景色由黄渐变为白  Puff  元素逐渐扩散然后消失。  Squish 元素逐渐缩小然后消失。 2.Effect.Scale 单击放大 ·29· ·29· Ruby on Rails 笔记  V0.1 (作者:陈刚) http://www.chengang.com.cn 单击一次文本放大一倍,再单击再放大。如把 200 改为 50,则为缩小;改为 100,则无变化。另外, 文本缩放要求用 em 为单位。 <%=content_tag("div","AAAAAAAAAAAAAAAA", :style=>"font-size:2.0em;width:100px;", :onclick=>"new Effect.Scale(this,200)")%> 这是图片的: <%= image_tag("rails.png",:onclick=>"new Effect.Scale(this,200)") %> 5.5 DOM 操作 1.El em ent.setContentZoom 非动 画方式缩 放 要求缩放目录用 em 为单位。这里的 200 不是象 Effect.Scale 所指那样放大一倍,而是指大小变为 2.0em。 <%=link_to_function("Small","Element.setContentZoom('id1',200)")%> 2.El em ent.show/hide/remove 显示 /隐藏/删除 Element.show('id1') 3.El em ent.toggle 显示 /隐藏之 间切换 Element.show('id1', ’id2’, ‘id3’) #同时操控三个元素 第 6 章 实际应用 6.1 购物车的实现 第一步 :创建布 局模板 app/views/layouts 目录中创建一个与控制器同名的模板文件 store.rhtml,则控制器下所有网页都 会使用此模板。文件 store.rhtml 的内容如下: <html> <head> <title>Pragprog Books Online Store <%= stylesheet_link_tag "scaffold", "depot", :media => "all" %>
Home
Questions
News
·30· Ruby on Rails 笔记 V0.1 (作者:陈刚) http://www.chengang.com.cn Contact
<% if @flash[:notice] -%>
<%= @flash[:notice] %>
<% end -%> <%= @content_for_layout %>
说明:  <%= stylesheet_link_tag "scaffold", "depot", :media => "all" %>生成指向 scaffold.css 、depot.css 两个式样表的标签  @page_title 各个页面标题变量  @flash[:notice] 提示性的信息(key=notice),这是一个公共变量。  <%= @content_for_layout %> 位置嵌入显示控制器内名网页。由于其他网页变成了模板的一部 份,所以其等标签应该去掉。 第二步 :创建 store_controller.rb。 当从 session 里取出某购物车不存在,则新建一个,并存入 session。最后把此购物车赋给变量 cart。 def find_cart @cart = (session[:cart] ||= Cart.new) end 第三步 :创建一 个 LineItem 的数据模型 。 使用“ruby script/generate model LineItem”命令,还会附带创建相应的测试程序。 store_controller.rb: def add_to_cart product = Product.find(params[:id]) @cart.add_product(product) redirect_to(:action => 'display_cart') rescue logger.error("Attempt to access invalid product #{params[:id]}") redirect_to_index('Invalid product') end 说明:  params 是 URL 的参数数组  redirect_to 转向语句  rescue 异常,相当于 Java 的 Exception  redirect_to_index 是 application.rb 中的一个方法,这个文件里的方法是各控制器公开的。  logger 是 rails 内置的变量。 第四步 :创建一 个内部使 用(和数 据库无关 )的数据 模型 class Cart attr_reader :items attr_reader :total_price def initialize empty! end def add_product(product) item = @items.find {|i| i.product_id == product.id} ·31· ·31· Ruby on Rails 笔记  V0.1 (作者:陈刚) http://www.chengang.com.cn if item item.quantity += 1 else item = LineItem.for_product(product) class Cart # An array of LineItem objects attr_reader :items # The total price of everything added to this cart attr_reader :total_price # Create a new shopping cart. Delegates this work to #empty! def initialize empty! end # Add a product to our list of items. If an item already # exists for that product, increase the quantity # for that item rather than adding a new item. def add_product(product) item = @items.find {|i| i.product_id == product.id} if item item.quantity += 1 else item = LineItem.for_product(product) @items << item end @total_price += product.price end # Empty the cart by resetting the list of items # and zeroing the current total price. def empty! @items = [] @total_price = 0.0 end end end @total_price += product.price end def empty! @items = [] @total_price = 0.0 end end ·32· Ruby on Rails 笔记 V0.1 (作者:陈刚) http://www.chengang.com.cn Ruby 语言入门 第 1 章 起 步 1.1 下载 Ruby ……略 1.2 执行 Ruby 脚本 (1)ruby –e ‘print “Hello,world!!”’ #-e 表示后面一行为 ruby 脚本 (2)进入命令行交互环境命令:irb;退出:exit (3)运行脚本:ruby hello.rb (4)用 Ruby 自带的 SciTE。把 locale.properties 复制到 ruby\scite 目录下,可以让 SciTE 界面变成 中文。执行 F5 第 2 章 语法 2.1 注释与分行 1.注释 # 单行注释用符号#起头 =begin 多行注释可以用=begin(之前不能有空格)和=end 来包裹 这也是内嵌文件 RDoc 的注释。 =end 2.分行 p "chengang";p "world" #用分号来分隔语句 p "abc"    #如果一行为一个语句则可以省略分号 p "chen\ #如果一条语句要分成两行写,则可以“\”连接 gang" 3.和 java 不同,ruby 方法的括号可要可不要。 p "chengang" #p 是一个方法,这两条语句是一样的。 p("chengang") ·33· ·33· Ruby on Rails 笔记  V0.1 (作者:陈刚) http://www.chengang.com.cn 2.2 关键字、标识、运算符 1.在给 类、方法 、变量等 起名时应 该尽量避 开关键字 。  模块定义:module  类 定义:class  方法定义:def、undef  检查类型:defined?  条件语句:if、then、else、elsif、case、when、unless  循环语句:for、in、while、until、next、 break、do、redo、retry、yield  逻辑判断:not、and、or  逻辑值和空值:true、false、nil  异常处理:rescue、ensure  对象引用:super、self  块的起始:begin、end  嵌入模块:BEGIN、END (注:BEGIN 模块相当于 C 语言中的宏,END 模块用来作一些收尾工 作。有了 require,include,应该取消 BEGIN 和 END 的语法定义。)  文件相关:_FILE_、_LINE_  方法返回:return  别  名:alias 2.标识 名称的约 定: 命名约定:  局部变量、方法参数、方法名称用“小写字母”或“下划线”开头。  全局变量前缀为$;实例变量前缀为@;类变量前缀为@@  类名、模块名、常量用“大写字母”开头  词首字母之后可以是字母、数字、下划线。@的下一个字符不能是数字  常量可以定义在类和模块中,不能定义在方法中。 命名建议:  常量全用大写的字母,用下划线分割单词。例如:MAX, ARRAY_LENGTH。  类名、模块名用大写字母开头的单词组合而成。例如:MyClass, Person。  方法名全用小写的字母,用下划线分割单词。例如:talk, is_prime?。在 Ruby 里,有时将“!”和“? ”附于某些方法名后面。惊叹号“!”暗示这个方法具有破坏性, 有可能会改变传入的参数。问号“? ”表示这个方法是一个布尔方法,只会返回 true 或 false。  变量和参数用小写字母开头的单词组合而成。例如:name, currentValue。  类名、模块名、变量名、参数名最好使用“名词”或者“形容词+名词”。方法名最好使用“动词” 或者“动词+名词”。例如:aStudent.talk 。 3.运算 符 运算符表(优先级由高到低排列) 能否重写 运算符 描述 Y [] []= 数组下标  数组元素赋值 Y ** 乘冥 Y ! ~  +  - 非 位非 一元加 负号 Y * / % 乘 除 模 Y + - 加 减 Y >> << 右移 左移 Y & 位与 ·34· Ruby on Rails 笔记 V0.1 (作者:陈刚) http://www.chengang.com.cn Y ^ | 位异或 位或 Y <=  <  >  >= 小于等于 小于 大于 大于等于 Y <=>  ==  ===  =~  !=  !~ 各种相等判断(!=  !~ 不能重写) && 短路与 || 短路或 ..  ... 区间的开始点到结束点 ? : 三元条件运算符 =  %=  ~=  /=  -=  +=  |= &=  >>=  <<=  *=  &&=  ||= **= 各种赋值 例如:a = 5; b += 3( 意思是:b = b+3 ); defined? 检查类型 not 逻辑非 or  and 逻辑或 逻辑与 if  unless  while  until 判断与循环 begin/end 定义方法、类、模块的范围 2.3 变量和数据类型 1.全局 变量 作用范围是整个程序。 变量命名前缀:$ 初始值为 nil(空对象) Ruby 语言中的系统变量都是全局变量 2.局部 变量 定义局部变量时,必须进行赋值。 3.self self 变量的值或者是当前正在执行的对象,或者为 nil。从值的形式上来看,self 象是局部变量,但 ruby 事实把 self 解释为一个全局变量。 4.实例 变量 作用范围是其所在的对象内。 变量命名前缀:@ 初始值为 nil(空对象) 5.符号 符号用冒号做前缀,它实际是一个常量。比如:id 的意思就是“名字叫做 id 的东西”。 6.数据 类型 数字分为整数型(1,0,75 ,1e3),浮点型(2.4 ,7.0 ,0.99)。浮点型数据小数点后必须跟数字 (1.e3 不可以,1.1e3 可以)。数字可以有前缀:0 表示八进制, 0x 表示十六进制, 0b 表示二进制 (0724,0x5AC4,0b11101)。 字符串是在单引号、双引号之间的字符。 数组的下标从 0 开始。数组元素可以是不同类型:[ 2.4, 99,“thank you”,[ a, b ,c ] ,78 ]。 区间:1..5 表示 1,2,3,4,5 ;1...5 表示 1,2,3,4 。 ·35· ·35· Ruby on Rails 笔记  V0.1 (作者:陈刚) http://www.chengang.com.cn 2.4 赋值和条件运算 1.赋值 a = 1 ;b = 2 + 3    # a=1 ,b=5 a ,b = b ,a       # a=5 ,b=1 a = b = 1 + 2 + 3    # a=6 ,b=6 a = (b = 1 + 2) + 3    # a=6 ,b=3 x = 0 # x=0 a,b,c = x, (x+1), (x+2) # a=0 ,b=1,c=2 2.条件 运算符 nil 和 false 为假,其他都为真 == (等于) != (不等于) 比较两个对象的值是否相等,返回true, flase a=1; b=1.0; a==b #true eql? 比较两个对象的值、类型是否相等,返回true, flase a=1; b=1.0; a.eql?(b) #flase(a为整数型,b为浮) equal? 比较两个对象在内存中地址是否相同(是否为同一对象实例),返回true, flase a=1.0; b=1.0; a.equal?(b) #flase a=1.0; b=a ; a.equal?(b) # true <=> 比较两个对象的大小:大于、等于、小于分别返回1,0,-1 "aab" <=> "acb"  # -1 (第二个a 的ASCII 码小于c) [5] <=> [4,9] # 1(第一个元素5 > 4) === 右边的对象是否在左边区间之内,返回true, flase (0..9)=== 3.14 #true ('a'..'f')=== 'c'  # true =~(匹配) 是否符合一个正则表达式。返回模式在字符串中被匹配到的位置,否则返回nil 例: !~ (不匹配) 断言不符合一个正则表达式,返回true, flase <=  <  >  >= 小于等于 小于 大于 大于等于 注: “==”和 equal 的含义,ruby 与 java 正好倒了过。 2.5 条件判断语句 1.if(如果) if 条件 then 语句… end (语句…) if 条件 if 条件   …… elseif 条件 …… else …… end 2.unless(除非 ) unless 的语句结构和 if 一样,条件判断相反。 3.case ·36· Ruby on Rails 笔记 V0.1 (作者:陈刚) http://www.chengang.com.cn x=3 case x when 1..2 print "a" when 4..9, 0 #4~9 和 0 print "b" else print "c" end 2.6 循环语句 1.while(当 …) (语句…) while 条件 while 条件 语句… end 2.until(直到 …) 语句结构和 while 一样,条件判断相反。 3.for…in for 变量 in 对象(如数组) 语句… end 4.其他 类型的循 环 3.times { print "Hi!" }    #Hi!Hi!Hi! 或者 3.times do print "Hi!" end 9.downto(1){|i| print i if i<7 }    #654321 1.upto(9) {|i| print i if i<7 }    #123456 (1..9).each {|i| print i if i<7}    #123456 0.step(11,3) {|i| print i }    #0369 5.其他 语句 break 跳出本层循环体 next 忽略本次循环的剩余部分,开始下一次的循环 redo 忽略本次循环的剩余部分,重新开始本次循环 retry 忽略本次循环的剩余部分,从头开始本层循环体 ·37· ·37· Ruby on Rails 笔记  V0.1 (作者:陈刚) http://www.chengang.com.cn 2.7 异常与线程 与 Java 中的 try…catch…finally…throw 相对应, Ruby 中用 begin …rescue…ensure … raise… end 来处理异常,retry 可以用在 rescue 中。可以只用 rescue 或是 ensure,两者都使用时,rescue 必须 在 ensure 前。 如果你初识 Ruby,不必理会异常与线程。 2.8 迭代器、代码块、闭包 1. 迭代器 (1..9).each {|i| print i if i<7} 迭代器 each 是数组类的一个方法;大括号{ }里的代码是代码块,简称块。你可以用大括号{ }将代码 组织成块,也可以用 do…end 将代码组织成块。大括号{ }的优先级高于 do…end。 2. 代码块 调用一个块要用关键字 yield。每一次 yield,块就被调用一次。 def one_block yield yield yield end one_block { puts "This is a block. " } 运行结果: This is a block. This is a block. This is a block. yield 还可以带参数调用块。 def one_block for num in 1..3 yield(num) end end one_block do |i| puts "This is block #{i}. " end 运行结果: This is block 1. This is block 2. This is block 3. 3. 闭包 闭包也是一段代码,一个代码块,而且能够共享其它方法的局部变量。闭包既然是一段代码,也就有 自己的状态,属性,作用范围,也就是一个可以通过变量引用的对象,我们称之为过程对象。一个过程对 象用 proc 创建,用 call 方法来调用。 def method(pr) ·38· Ruby on Rails 笔记 V0.1 (作者:陈刚) http://www.chengang.com.cn puts pr.call(7) end oneProc=proc{|k| k *=3 } #闭包 method(oneProc) # 显示 31 闭包共享其它方法局部变量的例子; def method(n) return proc{|i| n +=i } #将闭包创建于方法内部 end oneProc=method(3) puts oneProc.call(9) #12 puts oneProc.call(5) #17 2.9 元编程 你编写了一个能够生成其它程序的程序,那么,你就在做元编程的工作。 require "E9-1" class Person < MetaPerson end person1 = Person.new person2 = Person.new person1.sleep #sleep, sleep, sleep... person1.running #running, running, running... person1.modify_method("sleep", "puts 'ZZZ...'") person1.sleep # ZZZ... person2.sleep # ZZZ... #E9-1.rb class MetaPerson def MetaPerson.method_missing(methodName, *args) name = methodName.to_s begin class_eval(%Q[ def #{name} puts '#{name}, #{name}, #{name}...' end ]) rescue super(methodName, *args) end end def method_missing(methodName, *args) MetaPerson.method_missing(methodName, *args) send(methodName) end def MetaPerson.modify_method(methodName, methodBody) class_eval(%Q[ def #{methodName} #{methodBody} end ·39· ·39· Ruby on Rails 笔记  V0.1 (作者:陈刚) http://www.chengang.com.cn ]) end def modify_method(methodName, methodBody) MetaPerson.modify_method(methodName, methodBody) end end 程序 E9-1.rb 是一个能够生成其它程序的程序,你可以在 E9-1.rb 中将类名改换,只要在其它子类继 承就可以了。其它子类调用方法时,如果是已有的方法,则正常调用;如果是不存在的方法,就由 E9-1.rb 中的超类自动生成;如果你觉得方法不合你意,你能够很轻易地修改方法体,这个工作也由 E9-1.rb 中的 超类自动完成。E9-1.rb 中的超类是元编程的一个简单展现。你加入自己修改后的代码,能够使这个程序更 加智能化。 第 3 章 面向对象 3.1 类的例子 class Person attr_writer :motherland #类的属性(可写)、attr_accessor(可读写)、attr_reader(可读) def initialize( name, age=18 ) #构造函数,age 参数默认值 18 @name = name @age = age @motherland = "China" end #初始化方法结束 def talk puts "my name is "+@name+", age is "+@age.to_s if @motherland == "China" puts "I am a Chinese." else puts "I am a foreigner." end end # talk 方法结束 end # Person 类结束 p1=Person.new("kaichuan",20) p1.talk p2=Person.new("Ben") p2.motherland="ABC" p2.talk 程序的输出结果是 my name is kaichuan, age is 20 I am a Chinese. my name is Ben, age is 18 I am a foreigner. ·40· Ruby on Rails 笔记 V0.1 (作者:陈刚) http://www.chengang.com.cn 3.2 继承 class Student < Person 在子类中调用父类方法:super ruby 只有重写和多态,但没有重载。 重写:父类方法被子类改写。 多态:父类方法被不同子类重写,虽然方法名、参数等相同,但包含代码逻辑不同。 重载:同一个方法名和返回值,但参数不同。 3.3 方法 3.3.1 方法示例 从 ruby 的方法形式,可以看到象 java 那样的重载对于 ruby 是没有必要的。另外,一个类中可以有 两个同名方法,但只有最后那个方法有效。 用感叹号做方法名后缀,表示这是一个会对接收者造成破坏的方法,如:empty! 用问号做方法名后缀,表示这是一个返回布尔型值的方法,如:empty? (1)可接 收默认参 数 def sum( a, b=5 ) a+b end puts sum(3,6)  #9 puts sum(3)  #8 (2)支持 可变参数 def sum( *num ) result = 0 num.each { |i| result+=i } return result end puts sum()   #0 puts sum(3,6)    #9 puts sum(1,2,3,4,5,6,7,8,9)    #45 (3)类方 法 def 类名.方法名 或   def self.方法名 (4)return 语句 在方法最后一行可以用 return 返回某个值或几个值,但这不是必须的。最后一行如果是表达式,表达 式的值没有 return 也会被自动返回;最后一行语句如果不是表达式,就什么也不返回。return 的其他用法 和 java 中的一样。 3.3.2 单个实例重写方法,及增加方法 class Person def talk ·41· ·41· Ruby on Rails 笔记  V0.1 (作者:陈刚) http://www.chengang.com.cn puts "Hi! " end end p1=Person.new p2=Person.new def p2.talk #定义单例方法 p2.talk puts "Here is p2. " end def p2.laugh #定义单例方法 p2. laugh puts "ha,ha,ha... " end p1.talk # Hi! p2.talk # Here is p2. p2.laugh # ha,ha,ha... 3.3.2 方法的访问控制 public 可以被任何实例对象调用,不存在访问控制; 方法默认都是公有的( initialize方法除外,它永远是私有的)。 protect ed 可以被定义它的类和子类访问,不能在外部被实例对象访问,但是可以在类和子类内部被实例对象访 问。 private 可以被定义它的类和其子类访问,不能被实例对象访问。 3.4 模块 模块与类非常相似,也有方法和常量,但是:  模块不可以有实例对象;  模块不可以有子类。 puts Math.sqrt(2) # 1.4142135623731 模块名.方法名 puts Math::PI # 3.14159265358979 模块名::常量名 1.模块示例 1 module Me def sqrt(num, rx=1, e=1e-10) num*=1.0 (num - rx*rx).abs 100, "name"=>"chengang"} #键名有:id 和"id”两种用法,"chengang"也可以写成:chengang puts h.class #Hash puts h.length #2 puts h[:id] #用"id"打印为 nil puts h["name"] #用:name 打印为 nil h["name"] = "giles" #重新设值 puts h["name"] ·45· ·45· Ruby on Rails 笔记  V0.1 (作者:陈刚) http://www.chengang.com.cn 4.4 字符串 1.生成 字符串 str1 = 'this is str1' str3 = %q/this is str3/ # %q 用来生成单引号字符串。%q 后面的分隔符可以是! !;/ /;< >;( );[ ];{ };等等。 str4 = %Q/this is str4/ # %Q 用来生成双引号字符串 #从<<开始,是一个字符串文档 str5 = <
还剩48页未读

继续阅读

下载pdf到电脑,查找使用更方便

pdf的实际排版效果,会与网站的显示效果略有不同!!

需要 15 金币 [ 分享pdf获得金币 ] 10 人已下载

下载pdf

pdf贡献者

fly518

贡献于2011-11-01

下载需要 15 金币 [金币充值 ]
亲,您也可以通过 分享原创pdf 来获得金币奖励!
下载pdf