?
RoR是一个比较神奇的东西,首先建立在一个神奇的语言Ruby之上,有点颠覆我们过去对编程语言的认识(甚至包括一些面向对象的语言),Rails更发扬光大了这一点,其设计者简直是个软件架构的天才,他制定了Rails的“宪法”,使整个开发工作简洁、高效又不失灵活性。
如果把编程当成盖房子,Ruby就是盖房子的材料,过去的编程方式是盖一个砖结构的房子,使用的材料是砖块,方式是用砖块堆垒起来;而Ruby就是新型材料预制件,通过预先做好的连接组件搭起来。
Ruby的创始人是一个日本人,Yukihiro “matz” Matsumoto,他经常说的两句话表明了Ruby的特点和诉求:
"Trying to make Ruby natural, not simple” --更自然而不是简单
?
"Ruby is simple in appearance, but is very complex inside, just like our human body".--就像我们的身体一样,看似简单,其实复杂神奇。
?
Rails哲学
Rails的设计目标是提供一个简洁、高效的Web应用开发框架,当然所有的框架都是这么说的,但Rails有自己的价值观和哲学,让我们回顾一下,其实非常简单,就两条:
--DRY 不要重复自己,开发是最大限度的考虑代码的重用,代码的迁移,也包括重用现有和别人的代码
--COC 约定胜于配置,Rails抽象了绝大部分应用开发的过程和需求,并提供了自以为优秀的解决方案和框架,杜绝的大部分配置工作和冗余无效信息,实际上也确实做到了这一点,随着使用的深入,我们会深深的理解这一点。
例如,如何声明一个变量是本地变量、进程变量还是全局变量:
var,@var,$var
就可以了!
再如,如何定义一个函数(方法)的返回值,用return吗?No.
方法中的最后一个值就是返回值!!
简单吧!现实中有一个很好的比对例子是jquery(一个javascript框架)。
以上是官方的说法,从我看来,Rails还有两个核心哲学观:
1. 简洁、简洁、到简无可简
Rails不鼓励使用冗长的代码实现复杂的特性,从用户使用的角度来说也希望应用简单、快速并容易理解和使用。这让我想起了一个对”完美“的诠释:"完美不是增无可增,而是减无可减"。
?
2. 更接近人的表述和思维方式
看大师们的代码就像看到一个非常有条理的人在说话,一切都是那么自然和流畅。
?
?
自带开发环境
我们过去开发,首先是考虑IDE(集成开发环境和工具),需要编译器、类库、扩展模块、Web服务、数据库服务、测试支持、调试环境、多语言、版本控制、协作开发....,搭建一个完整的开发环境需要来自不同厂商和技术的很多软件,将它们组装在一起并协调工作本来就是一个比较挑战的工作。
Rails就比较简单了,安装完成后,这些东西基本上都有,如果需要扩展,也可以在线或者自动(使用bundler技术)来进行扩展,只需要一个命令。这样,我们只需要一个比较好的编辑器、命令行程序、和浏览器,就可以进行开发了,这个也能够保证我们的开发和部署支持跨平台和异构环境。
?
一切都是对象
我们通常说某语言是面向对象的,比如Java,但仔细相信,好像Java也有一些基本类型,Ruby在这方面走的更远,基本上完全对象化了,由此我们可以看到更简洁神奇的代码:
5.times { print "我们爱 Ruby -- it's outrageous!" }
上面的代码打印五次“我们爱Ruby”,5这时是一个对象,调用了times方法。你甚至可以改编这个类,如添加一个Plus方法:
?
class Numeric
def plus(x)
self.+(x)
end
end
?
y = 5.plus 6
# y is now equal to 11
?
?
再如,如果要倒排一个字符串,应该怎么处理呢?:
"Jimmy".reverse
"Jimmy".length
?
数组方法:
[12, 47, 35].max
?
?
?
简约而不简单
?
看看下面一行代码,叫块( do ... end),实现遍历,生成一个数组。
?
search_engines =%w[Google Yahoo MSN].map do |engine|
"http://www." + engine.downcase + ".com"
end
?
列出目录下的文件:
Dir.entries "/"
给数字排序:
[12, 47, 35].sort
?
?
文件修改时间的小时数:
File.mtime("/Home/comics.txt").hour
下面定义了一个函数,参数是某个文件,函数遍历文件的所有行,将用冒号分隔的名称和地址保存在一个哈希集合中并返回:
?
def load_comics( path )
comics = {}
File.foreach(path) do |line|
name, url = line.split(': ')
comics[name] = url.strip
end
comics
end
?
?
迭代器Iterator
?
迭代器是对象语言的一个非常重要的概念,表示一个对象集合,也是非常重要的一个功能。如何实现处理集合中的对象(如遍历)也是程序语言的一个非常重要的特性,Ruby实现的简洁而直观,可以使用each方法:
#!/usr/bin/ruby
ary = [1,2,3,4,5]
ary.each do |i|
puts i
end
?
更简单的是使用collect方法:
#!/usr/bin/ruby
a = [1,2,3,4,5]
b = Array.new
b = a.collect
c = a.collect{|x| 10*x}
puts b
puts c
?
?
命令行界面
?
图形界面可能代表直观,但不一定(通常不)代表高效。例如:
rails new demo
这个命令创建了一个名为demo的应用程序,想想我们在IDE里面怎么做的吧,新建项目-起个名字-项目模版-程序设置....-保存,大概要点十几下鼠标吧。另一个例子是为当前应用程序启动Web服务器:
rails server
或者启动单元测试:
rake test:unit
?
?
?
?
?
最简化的配置
?
先想想struts中的配置吧,web-config,action,class,tag lib,result,map ...。Rails中就简单太多了。
Rails自认为了解你开发的需求,并且做出了很好的安排,消除了大量冗余信息和多余环境,也降低了错误的几率,就大大简化了开发过程中的配置:
?
?
--命名方式
Rails会自动处理类名、控制器名、视图(模版)名、对应的文件名、数据库名...,甚至可以很好的处理单数和复数(如表名是类名的复数)
-- 在控制器中定义重定向
?
即便如此,我们还是需要做一些配置工作:
--数据库配置
位于 config/database.yml 文件中,在此可以一次定义开发、测试、生产三种环境中的数据库连接配置。
?
-- 应用根路径和路径映射
位于 config/route.rb 文件中,通常需要定义程序首页(系统默认的首页是一个静态文件 public/index.html),其他的路径映射通常会自动配置,需要时可能要做相应调整。
?
?
?
?
?
?
生成器
Rails中有一个强大工具,生成器,使用简单的命令行,就可以创建和修改M、V、或者C。
想想我们过去是怎么建立模型的吧,我们会编辑一个文件,创建类,定义属性和方法,然后为模型创建数据库表,进行配置和映射...,如果要修改呢,再来一遍...
我们看看Rails怎么处理,执行两个命令就可以了:
?
rails generate model Product title:string description:text image_url:string price:decimal
rake db:migrate
第一个命令利用创建一个product模型,包括所有相关的类定义文件,第二个命令在此基础上真正创建数据库结构。有趣的是在模型文件中我们看不到属性的定义(应该在数据库表定义中吧),而且在数据库结构中自动创建了一些字段如自增id,创建和修改时间等等。要修改这个模型:
rails generate migration add_category_to_product categroy:integer
rake db:migrate
Rails会明白,在product模型(数据表)中添加一个categroy字段。
?
如果还想更省事,可以使用脚手架scaffold,它可以一次生成MVC:
?
rails generate scaffold Product title:string description:text image_url:string price:decimal
这个命令可以生成一个完整的信息类应用程序架构:
项目列表-项目详情-项目添加-项目修改-项目删除,系统自动生成所有相关的视图(列表视图、详情视图、增加表单、编辑表单)和操作(增加、修改、删除)和数据库模型。
?
?
?
?
?
?
?
动态方法
看一个查找方法的例子,在模型中显然没有定义这样的方法,但Rails就知道可以通过增加的后缀来进行处理,很直观,很聪明了吧?!
Order.find_by_name("Dave Thomas")
?
Order.find_all_by_name("Dave Thomas")
Order.find_all_by_email("john@gmail.com")
?
?
?
?
模型关联
在Rails中定义对象关系很神奇也很简单,只需要在模型定义中增加声明就可以了,系统会自动处理相关的依赖性,如
--订单和发票的一对一关系:
订单模型中: has_one: invoice
发票模型中: belongs_to: order
?
--订单和项目的一对多关系
?
订单模型中: has_many: line_items:dependent => :destroy
项目模型中: belongs_to: order
?
?
--分类和产品的多对多关系
?
分类模型中: has_and_belongs_to_many :products
产品模型中: has_and_belongs_to_many :categroies
?
?
--利用中间模型实现的多对多关系,可以中间模型加入一些处理和逻辑
?
订单项目模型:
belongs_to :order
belongs_to :product
?
订单模型中: has_many: line_items:dependent => :destroy
产品模型中: has_many: line_items
?
?
?
?
?
?
视图部件
?
我们经常会遇到需要循环显示一个数组或者集合的情况,例如下面的视图定义显示购物车项目:
?
<table>
<% for item in @cart.line_items %>
<tr>
<td><%= item.quantity %>×</td> <td><%= item.product.title %></td> <td class="item_price"><%= number_to_currency(item.total_price) %></td>
</tr>
<% end %>
<tr class="total_line"> <td colspan="2">Total</td> <td class="total_cell"><%= number_to_currency(@cart.total_price) %></td>
</tr>
</table>
Rails使用循环标签 for item in items...end 来显示items集合中所有的元素,一行一个,看起来不错。但我们有更好的方式:
?
<table>
<%= render(@cart.line_items) %>
<tr class="total_line"> <td colspan="2">Total</td> <td class="total_cell"><%= number_to_currency(@cart.total_price) %></td>
</tr>
</table>
这里只需要渲染一个集合cart.line_items,当然我们需要做些其他的工作,在相应视图文件夹中创建一个_line_item.html.erb文件就可以了:
?
<tr>
<td><%= line_item.quantity %></td>
<td><%= line_item.product.title %></td>
<td class="item_price"><%= number_to_currency(line_item.total_price) %></td>
</tr>
这里我们创建一个视图部件_line_item.html.erb,渲染时Rails知道会调用这个视图模版,并且自动循环显示。?
Javascript支持
在Web应用中适当引入javascript可以大大提升应用体验,Rails也可以集成javascript技术(应该是procotype库),看看下面的例子:
增加一个按钮,用于清空购物车:
<%= button_to '清空购物车', @cart, :method => :delete%>
?
通常我们会提示用户确认,我们通常希望调用一个js confirm函数,而利用Rails,这里需要增加一个confirm成员信息就可以了,Rails会使用JS在客户端实现:
<%= button_to '清空购物车', @cart, :method => :delete,:confirm => '你确认吗?'%>
JS的另一项热门技术是AJAX,我们看看Rails怎么处理。 还是以购物车为例,设想在商品项目点击添加到购物车,项目会调用AJAX添加到购物车列表中,页面不需要刷新。要实现这个,基本上需要四个设置: 1. 在页面中的合适位置定义一个内容块,将来AJAX调用返回的内容将显示在这个块之中 <div id="cart"> <%= render @cart %> </div> 2. 告诉按钮提交使用remote设置(添加成员:remote => true) <%= button_to '添加到购物车', line_items_path(:product_id => product),:remote => true %> 2. 在控制器的create方法中增加提供js格式的数据输出,和remote提交对应 if @line_item.save format.html { redirect_to(store_url) } format.js format.xml { render :xml => @line_item,:status => :created, :location => @line_item } else ... 4. 定义js输出内容,?
需要视图目录中创建一个create.js.rjs的文件,内容如下:
page.replace_html('cart', render(@cart))
意思是将渲染内容输出并刷新对应区块的内容。
我们看到,Rails的实现不需要改变程序框架和结构,不需要修改客户端内容,也不需要编写和调用AJAX代码,确实令人耳目一新!
?
I18n国际化
全球经济一体化使我们的程序有机会在全世界范围内被使用,一个良好的框架必须对此有良好的支持,而Rails的实现方式也是那么简单而优美:
1. 首先为不同的语言定义资源文件,格式为yml
2. 做好相应的设置,如声明包含这些资源文件和设置默认语言
3. 在程序中使用,最简单的方式是直接使用神奇的t 对象:
<%= t('Title') %>
4. 当然还有更高级的用法,如基于域名/URL,基于视图的命名空间,系统本地化....
?