Francis's Octopress Blog

A blogging framework for hackers.

Nginx缓存cache的5种方案

nginx缓存cache的5种方案

1. 客户端浏览器上的缓存(非Cookie, Cookie中的内容为: 键和值均为string类型的键值对) 我们可以通过在Http回应中增加特定的头部说明来指定浏览器的缓存策略; 添加头部说明的手段既可以通过页面指令声明设置, 也可以通过编程方式设置.   对于图片、Javascript脚本、CSS等资源, 可以在IIS管理器中, 右击图片等资源, 选择”属性” –> HttpHeaders后, 勾选Enable Content Expiration并设置时间即可. 一种值得推荐的手段是, 将需要缓存的资源分类, 如: image/dynamic/、image/static/, 这样我们可以再文件夹上, 选择属性中的HttpHeaders进行设置. 否则, 针对每一个静态资源设置HttpHeaders将是件非常痛苦的事情. 此外, 还可以采用一款名为CacheRight的工具可以提供缓存资源的统一配置.   查看或设置浏览器缓存位置:  IE –> Internet选项 –> 常规 –> 临时Internet文件 –> 设置   Html文件的Head中的缓存设置: <meta http-equiv=”pragma” content=”no-cache” /> <meta http-equiv=”Cache-Control” content=”no-cache” /> <meta http-equiv=”expires” content=”Wed, 26 Feb 1997 08:21:57 GMT” /> 浏览器中关于Cache的3属性: Cache-Control: 设置相对过期时间, max-age指明以秒为单位的缓存时间. 若对静态资源只缓存一次, 可以设置max-age的值为315360000000 (一万年). Http协议的cache-control的常见取值及其组合释义: no-cache: 数据内容不能被缓存, 每次请求都重新访问服务器, 若有max-age, 则缓存期间不访问服务器. no-store: 不仅不能缓存, 连暂存也不可以(即: 临时文件夹中不能暂存该资源) private(默认): 只能在浏览器中缓存, 只有在第一次请求的时候才访问服务器, 若有max-age, 则缓存期间不访问服务器. public: 可以被任何缓存区缓存, 如: 浏览器、服务器、代理服务器等 max-age: 相对过期时间, 即以秒为单位的缓存时间. no-cache, private: 打开新窗口时候重新访问服务器, 若设置max-age, 则缓存期间不访问服务器. private, 正数的max-age: 后退时候不会访问服务器 no-cache, 正数的max-age: 后退时会访问服务器 点击刷新: 无论如何都会访问服务器. Expires: 设置以分钟为单位的绝对过期时间, 优先级比Cache-Control低, 同时设置Expires和Cache-Control则后者生效. Last-Modified: 该资源的最后修改时间, 在浏览器下一次请求资源时, 浏览器将先发送一个请求到服务器上, 并附上If-Unmodified-Since头来说明浏览器所缓存资源的最后修改时间, 如果服务器发现没有修改, 则直接返回304(Not Modified)回应信息给浏览器(内容很少), 如果服务器对比时间发现修改了, 则照常返回所请求的资源.   注意: Last-Modified属性通常和Expires或Cache-Control属性配合使用, 因为即使浏览器设置缓存, 当用户点击”刷新”按钮时, 浏览器会忽略缓存继续向服务器发送请求, 这时Last-Modified将能够很好的减小回应开销.   ETag将返回给浏览器一个资源ID, 如果有了新版本则正常发送并附上新ID, 否则返回304, 但是在服务器集群情况下, 每个服务器将返回不同的ID, 因此不建议使用ETag.   以上描述的客户端浏览器缓存是指存储位置在客户端浏览器, 但是对客户端浏览器缓存的实际设置工作是在服务器上的资源中完成的. 虽然刚才我们介绍了有关于客户端浏览器缓存的属性, 但是实际上对这些属性的设置工作都需要在服务器的资源中做设置. 我们有两种操作手段对浏览器缓存进行设置, 一个是通过页面指令声明来设置, 另外一个是通过编程方式来设置.   浏览器缓存的设置手段: 第一: 通过页面指令声明来设置HTTP的缓存 页面指令<%@ OutputCache Location=”Any” Duration=”10” VaryByParam=”ProductId” VaryByHeader=”Accept-Language”%>中的Location用来设置缓存的位置, 该属性常见的值为: Any(默认): 输出缓存可以位于任何地点, 对应于HttpCacheability.Public. 如: 客户端浏览器、代理服务器或服务器本身. Client: 只能位于发出请求的客户端浏览器, 对应于HttpCacheability.Private. Downstream: 输出缓存可以位于除服务器本身的其他任何地方, 如: 客户端浏览器、代理服务器. Server: 输出缓存位于Web服务器本身, 对应于HttpCacheability.Server ServerAndClient: 输出缓存只能位于服务器本身或客户端浏览器, 对应于HttpCacheability.Private和HttpCacheability.Server None: 禁用输出缓存, 对应于HttpCacheability.NoCache. VaryByParam属性: 根据请求参数的不同而缓存不同的版本. 多个值用分号(;)分隔, *号表示为任意参数或参数组合缓存不同版本, “none”表示只缓存一个版本. VaryByHeader属性: 根据请求头来缓存不同的版本, 如同一页面的不同语言版本. VaryByCustom属性: 根据自定义参数来缓存不同的版本, 如: VaryByCunstom=”browser”是系统已实现的, 根据浏览器名称和版本号缓存不同的版本. 也可以, 根据自定义参数来缓存, 如: VaryByCustom=”happy”, 此时系统不知道如何解释happy, 因此需要在Global.asax或IHttpModule实现类中重写GetVaryByCustomString()方法, 来完成处理逻辑. VaryByControl属性: 根据用户控件中的服务器控件ID来缓存不同版本. 更高级的方式, 是通过配置文件来设置HTTP的缓存. 页面指令为<%@ OutputCache CacheProfile=”cacheconfig”%>, 其中cacheconfig是配置文件中的缓存配置节中CacheProfile的名称.
View Code
<system.web> <caching> <outputCacheSettings> <outputCacheProfiles> <add name=”cacheconfig” duration=”10” varyByParam=”none”/> </outputCacheProfiles> </outputCacheSettings> </caching> </system.web>

nginx缓存cache的5种方案

1、传统缓存之一(404) 这个办法是把nginx的404错误定向到后端,然后用proxy_store把后端返回的页面保存。 配置: location / { root /home/html/;#主目录 expires 1d;#网页的过期时间 error_page 404 =200 /fetch$request_uri;#404定向到/fetch目录下 } location /fetch/ {#404定向到这里 internal;#指明这个目录不能在外部直接访问到 expires 1d;#网页的过期时间 alias /home/html/;#虚拟目录文件系统地址要和locaion /一致,proxy_store会将文件保存到这目录下 proxy_pass ;#后端upstream地址,/fetch同时是一个代理 proxy_set_header Accept-Encoding ”;#让后端不要返回压缩(gzip或deflate)的内容,保存压缩后的内容会引发乱子。 proxy_store on;#指定nginx将代理返回的文件保存 proxy_temp_path /home/tmp;#临时目录,这个目录要和/home/html在同一个硬盘分区内 } 使用的时候还有要注意是nginx要有权限往/home/tmp和/home/html下有写入文件的权限,在linux下nginx一般会配置成nobody用户运行,这样这两个目录就要chown nobody,设成nobody用户专用,当然也可以chmod 777,不过所有有经验的系统管理员都会建议不要随便使用777。 2、传统缓存之二(!-e) 原理和404跳转基本一致,但更简洁一些: location / { root /home/html/; proxy_store on; proxy_set_header Accept-Encoding ”; proxy_temp_path /home/tmp; if ( !-f $request_filename ) {         proxy_pass ; } } 可以看到这个配置比404节约了不少代码,它是用!-f来判断请求的文件在文件系统上存不存在,不存在就proxy_pass到后端,返回同样是用proxy_store保存。 两种传统缓存都有着基本一样的优点和缺点: 缺点1:不支持带参数的动态链接,比如read.php?id=1,因为nginx只保存文件名,所以这个链接只在文件系统下保存为read.php,这样用户访问read.php?id=2时会返回不正确的结果。同时不支持这种形式的首页和二级目录,因为nginx非常老实,会将这样的请求照链接写入文件系统,而这个链接显然是一个目录,所以保存失败。这些情况都需要写rewrite才能正确保存。 缺点2:nginx内部没有缓存过期和清理的任何机制,这些缓存的文件会永久性地保存在机器上,如果要缓存的东西非常多,那就会撑暴整个硬盘空间。为此可以使用一个shell脚本定期清理,同时可以撰写php等动态程序来做实时更新。 缺点3:只能缓存200状态码,因此后端返回301/302/404等状态码都不会缓存,假如恰好有一个访问量很大的伪静态链接被删除,那就会不停穿透导致后端承载不小压力。 缺点4:nginx不会自动选择内存或硬盘作为存储介质,一切由配置决定,当然在当前的操作系统里都会有操作系统级的文件缓存机制,所以存在硬盘上也不需要过分担心大并发读取造成的io性能问题。 nginx传统缓存的缺点也是它和squid等缓存软件的不同之特色,所以也可看作其优点。在生产应用中它常常用作和squid的搭档,squid对于带?的链接往往无法阻挡,而nginx能将其访问拦住,例如:?和在squid上会被当做两个链接,所以会造成两次穿透;而nginx只会保存一次,无论链接变成还是,均不能透过nginx缓存,从而有效地保护了后端主机。 nginx会非常老实地将链接形式保存到文件系统中,这样对于一个链接,可以很方便地查阅它在缓存机器上的缓存状态和内容,也可以很方便地和别的文件管理器如rsync等配合使用,它完完全全就是一个文件系统结构。 这两种传统缓存都可以在linux下将文件保存到/dev/shm里,一般我也是这么做的,这样可以利用系统内存来做缓存,利用内存的话,清理过期内容速度就会快得多。使用/dev/shm/时除了要把tmp目录也指向到/dev/shm这个分区外,如果有大量小文件和目录,还要修改一下这个内存分区的inode数量和最大容量: mount -o size=2500M -o nr_inodes=480000 -o noatime,nodiratime -o remount /dev/shm 上面的命令在一台有3G内存的机器上使用,因为/dev/shm默认最大内存是系统内存的一半就是1500M,这条命令将其调大成2500M,同时shm系统inode数量默认情况下可能是不够用的,但有趣的是它可以随意调节,这里调节为480000保守了点,但也基本够用了。 3、基于memcached的缓存 nginx对memcached有所支持,但是功能并不是特别之强,性能上还是非常之优秀。 location /mem/ {     if ( $uri ~ “^/mem/([0-9A-Za-z_]*)$” )     {      set $memcached_key “$1”;      memcached_pass     192.168.1.2:11211;     }     expires 70; } 这个配置会将指明到memcached的abc这个key去取数据。 nginx目前没有写入memcached的任何机制,所以要往memcached里写入数据得用后台的动态语言完成,可以利用404定向到后端去写入数据。 4、基于第三方插件ncache ncache是新浪兄弟开发的一个不错的项目,它利用nginx和memcached实现了一部分类似squid缓存的功能,我并没有使用这个插件的经验,可以参考:   5、nginx新开发的proxy_cache功能 从nginx-0.7.44版开始,nginx支持了类似squid较为正规的cache功能,目前还处于开发阶段,支持相当有限,这个缓存是把链接用md5编码hash后保存,所以它可以支持任意链接,同时也支持404/301/302这样的非200状态。 配置: 首先配置一个cache空间: proxy_cache_path /path/to/cache levels=1:2 keys_zone=NAME:10m inactive=5m max_size=2m clean_time=1m; #注意这个配置是在server标签外,levels指定该缓存空间有两层hash目录,第一层目录是1个字母,第二层为2个字母,保存的文件名就会类似/path/to/cache/c/29/b7f54b2df7773722d382f4809d65029c;keys_zone为这个空间起个名字,10m指空间大小为10MB;inactive的5m指缓存默认时长5分钟;max_size的2m是指单个文件超过2m的就不缓存;clean_time指定一分钟清理一次缓存。 location / {     proxy_pass ;     proxy_cache NAME;#使用NAME这个keys_zone     proxy_cache_valid 200 302 1h;#200和302状态码保存1小时     proxy_cache_valid 301 1d;#301状态码保存一天     proxy_cache_valid any 1m;#其它的保存一分钟 } ps:支持cache的0.7.44到0.7.51这几个版本的稳定性均有问题,访问有些链接会出现错误,所以这几个版本最好不要在生产环境中使用。nginx-0.7下目前所知较为稳定的版本是0.7.39。稳定版0.6.36版也是近期更新,如果在配置里没有使用到0.7的一些新标签新功能,也可以使用0.6.36版。

Rubyonrails Guide to Active Record Associations

Rubyonrails Guide to Active Record Associations

Active Record

Active Record(中文名:活动记录)是一种领域模型模式,特点是一个模型类对应关系型数据库中的一个表,而模型类的一个实例对应表中的一行记录。Active Record 和Row Gateway (行记录入口)十分相似,但前者是领域模型,后者是一种数据源模式。关系型数据库往往通过外键来表述实体关系,Active Record 在数据源层面上也将这种关系映射为对象的关联和聚集。 Active Record 适合非常简单的领域需求,尤其在领域模型和数据库模型十分相似的情况下。如果遇到更加复杂的领域模型结构(例如用到继承、策略的领域模型),往往需要使用分离数据源的领域模型,结合Data Mapper (数据映射器)使用。 Active Record 驱动框架一般兼有ORM 框架的功能,但ActivActive Recorde Record 不是简单的ORM,正如和Row Gateway 的区别。著名的例子是全栈(Full Stack)Web 开发框架Ruby on Rails ,其默认使用一个纯Ruby 写成的Active Record 框架来驱动MVC 中的模型层。 对象关系映射(ORM)提供了概念性的、易于理解的模型化数据的方法。ORM方法论基于三个核心原则:简单:以最基本的形式建模数据。传达性:数据库结构被任何人都能理解的语言文档化。精确性:基于数据模型创建正确标准化了的结构。 在Martin Fowler 的《企业应用架构模式》一书中曾详细叙述了本模式。 以下是著名的Active Record 驱动框架: SQLObject(Python) Ruby on Rails ActiveRecord (Ruby) Yii Framework ActiveRecord (PHP) Castle ActiveRecord (.NET)

Migrations

Migrations are a convenient way for you to alter移动your database in a structured and organized manner.Migrations是一种很便捷的方法让你能够以一种结构化的和有组织的方式来迁移你的数据库。You could edit fragments of SQL by hand but you would then be responsible for telling other developers that they need to go and run them.你可以手动编辑SQL片段,而且你有责任把这些告诉其他的开发人员,因为他们需要开发和使用它们。You’d also have to keep track of which changes need to be run against the production machines next time you deploy.你也可以跟踪对你部署的代码在接下来的production机器(将会)发生的变化。 Active Record tracks which migrations have already been run so all you have to do is update your source and run rake db:migrate.Active Record跟踪并迁移你已经运行过的(代码和数据),而你只需要在更新了你的源代码的时候执行rake db:migrateActive Record will work out which migrations should be run.Active Recor将会计算出那些迁移需要被执行。It will also update your db/schema.rb file to match the structure of your database.它还会更新你的db/schema.rb文件使其于你的数据库结构相匹配。 Rails使用的是Active Record 框架来处理数据迁移,这里笔者把ActiveRecord框架放在一个地方学习了,如需了解Migration部分需要直接阅读Migration部分。  

Active Record Validations and Callbacks 活动记录验证和回调

This guide teaches you how to hook勾子into the life cycle of your Active Record objects.这个教程指导你怎样挂接到你的Active Record objects的生存周期。You will learn how to validate the state of objects before they go into the database, and how to perform custom operations at certain points in the object life cycle.你将会学习到在将数据对象存入数据库之前怎样验证它们的状态,以及在对象生存周期的一些点上怎样执行定制操作。 Rails使用的是Active Record 框架来处理验证和回调,这里笔者把ActiveRecord框架放在一个地方学习了,如需了解Migration部分需要直接阅读ValidationsandCallbacks部分。

A Guide to Active Record Associations

This guide covers the association features of Active Record. By referring to this guide, you will be able to:本教程涵盖了Active Record的关系部分的特性。(通过)这个教程提及的,你将能够:
  • Declare associations between Active Record models 在Active Record的models中声明(它们的)关系
  • Understand the various types of Active Record associations 明白各种类型的Active Record关系
  • Use the methods added to your models by creating associations 通过添加方法到models(的形式)来创建关系

1 Why Associations?为什么(会有)Associations

Why do we need associations between models? Because they make common operations simpler and easier in your code. For example, consider a simple Rails application that includes a model for customers and a model for orders. Each customer can have many orders. Without associations, the model declarations would look like this:为什么在models之间需要关系?因为它们使得在你的代码中大多数操作更加简单和容易。例如,思考一个简单的Rails应用程序其中包含一个customers的models和一个orders的模型(生产者和消费者模型)。每个消费者可以拥有多个生产者。没有associations(的话),模型声明看起来像这样: classCustomer<ActiveRecord::Base end classOrder<ActiveRecord::Base end Now,suppose we wanted to add a new order for an existing customer.We’d need to do something like this:现在假设我们想为一个存在的customer添加一个新的order。我们需要做如下事情: @order=Order.create(:order_date=>Time.now, :customer_id=>@customer.id) Or consider deleting a customer, and ensuring that all of its orders get deleted as well:或者考虑删除一个customer,那么确保它的所有的orders也被删除。 @orders = Order.where(:customer_id => @customer.id) @orders.each do |order| order.destroy end @customer.destroy With Active Record associations, we can streamline these — and other — operations by declaratively声明方式telling Rails that there is a connection between the two models. Here’s the revised code for setting up customers and orders:通过Active Record associations,我们可以精简这样的或者其它(类似的)操作通过声明的方式告诉Rails在两个models之间有一个连接。这里修订代码来设定customers和orders: class Customer < ActiveRecord::Base has_many :orders, :dependent => :destroy end   class Order < ActiveRecord::Base belongs_to :customer end With this change, creating a new order for a particular customer is easier:通过这样的修改,创建一个新的order给一个特定的customer更加容易: @order = @customer.orders.create(:order_date => Time.now) Deleting a customer and all of its orders is much easier:删除一个customer和它的所有的orders也容易了许多: @customer.destroy To learn more about the different types of associations, read the next section of this guide. That’s followed by some tips and tricks for working with associations, and then by a complete reference参考to the methods and options for associations in Rails.想要学习更多不同的类型的associations,阅读guide接下来的部分。这些(内容)伴随这一些在(使用)associations工作发现的tips和tricks,然后是一些Rails的associations的一些方法和options的一些完整的参考。

2 The Types of Associations

In Rails, an association is a connection between two Active Record models. Associations are implemented using macro-style calls, so that you can declaratively add features to your models. For example, by declaring that one model belongs_to another, you instruct Rails to maintain Primary Key–Foreign Key information between instances of the two models, and you also get a number of utility methods added to your model. Rails supports six types of associations:在Rails,一个association是一个在两个ActiveRecord模型之间的连接。Associations(通过)使用宏方式的调用实施,以至于你可以(以)声明的方式添加features到你的models。例如申明一个modelbelongs_to另一个(模型)。Rails支持六种类型的associations
  • belongs_to
  • has_one
  • has_many
  • has_many :through
  • has_one :through
  • has_and_belongs_to_many
In the remainder of this guide,youll learn how to declare and use the various forms of associations. Butfirst,a quick introduction to the situations where each association type is appropriate.在这个guide的其他的部分,你将会学习到怎样申明和使用各种的形式的associations。但是首先,做一个快速的说明:每种类型的association在什么情况下出现。

2.1 The belongs_to Association

A belongs_to association sets up a one-to-one connection with another model, such that each instance of the declaring model “belongs to” one instance of the other model. For example, if your application includes customers and orders, and each order can be assigned to exactly one customer, you’d declare the order model this way:一个belongs_to关系设立一个one-to-one连接到另一个model,通过这样声明model的每一个实例belongs to另一个模型的实例。例如,如果你的应用程序包含customersorders,并且每个order能够关联到仅仅一个customer,你可以这样的方式申明order模型: class Order < ActiveRecord::Base belongs_to :customer end

2.2 The has_one Association

A has_one association also sets up a one-to-one connection with another model, but with somewhat different semantics语义(and consequences结果). This association indicates that each instance of a model contains or possesses one instance of another model. For example, if each supplier in your application has only one account, you’d declare the supplier model like this: 一个has_one关系也设定一个one-to-one连接到另一个model,但是有某些不同的语义(和结果)。这个关系指出了(声明的)模型的每一个实例包含或拥有一个其他模型的实例。例如你的应用程序中的每一个supplier仅仅只有一个account,你可以这样声明supplier模型: class Supplier < ActiveRecord::Base has_one :account end  

2.3 The has_many Association

A has_many association indicates a one-to-many connection with another model. You’ll often find this association on the “other side” of a belongs_to association. This association indicates that each instance of the model has zero or more instances of another model. For example, in an application containing customers and orders, the customer model could be declared like this: 一个has_many关系指定了一个one-to-many连接到另一个model。你会经常发现这个关系在belongs_to关系的另一个端。这个关系指明了(声明)模型的每一个实例有零个或多个其它模型的实例。例如,在一个应用程序中包含customersorderscustomer模型如下声明: class Customer < ActiveRecord::Base has_many :orders end The name of the other model is pluralized多元化when declaring a has_many association.  

2.4 The has_many :through Association

A has_many :through association is often used to set up a many-to-many connection with another model. This association indicates that the declaring model can be matched with zero or more instances of another model by proceeding through a third model. For example, consider a medical practice where patients make appointments to see physicians. The relevant association declarations could look like this: 一个has_many :through关系通常用于设定一个many-to-many连接到另一个模型。这个关系指定申明的模型可以通过第三个模型(中间模型)匹配零个或多个另一个模型的实例。例如,想想一个医疗实践(例子)其中病人约定时间去看医生。它们的有关关系声明可以如下: class Physician < ActiveRecord::Base has_many :appointments has_many :patients, :through => :appointments#通过约会有多个病人 end   class Appointment < ActiveRecord::Base belongs_to :physician belongs_to :patient end   class Patient < ActiveRecord::Base has_many :appointments has_many :physicians, :through => :appointments end The collection of join models can be managed via通过the API. For example, if you assign加入模型的集合可以通过API管理。例如,如果你指定 physician.patients = patients new join models are created for newly associated关联的objects, and if some are gone their rows are deleted. Automatic deletion of join models is direct, no destroy callbacks are triggered. The has_many :through association is also useful for setting up “shortcuts” through nested has_many associations. For example, if a document has many sections, and a section has many paragraphs, you may sometimes want to get a simple collection of all paragraphs in the document. You could set that up this way: has_many :through关系也有利于设定快捷方式通过嵌套has_many关系。 class Document < ActiveRecord::Base has_many :sections has_many :paragraphs, :through => :sections end   class Section < ActiveRecord::Base belongs_to :document has_many :paragraphs end   class Paragraph < ActiveRecord::Base belongs_to :section end With :through => :sections specified, Rails will now understand:因为:through => :sections被指定,Rails现在将会明白如下(语句): @document.paragraphs

2.5 The has_one :through Association

A has_one :through association sets up a one-to-one connection with another model. This association indicates that the declaring model can be matched with one instance of another model by proceeding through a third model. For example, if each supplier has one account, and each account is associated with one account history, then the customer model could look like this: 一个has_one :through关系设定一个one-to-one连接到另一个模型。这个关系指明声明的模型可以匹配另个模型的一个实例通过第三个模型(中间模型)。例如,如果每个supplier(供应商)有一个account,并且每个account关联一个account history,那么customer模型将会像这样: class Supplier < ActiveRecord::Base has_one :account has_one :account_history, :through => :account end   class Account < ActiveRecord::Base belongs_to :supplier has_one :account_history end   class AccountHistory < ActiveRecord::Base belongs_to :account end  

2.6 The has_and_belongs_to_many Association

A has_and_belongs_to_many association creates a direct many-to-many connection with another model, with no intervening干预model. For example, if your application includes assemblies and parts, with each assembly having many parts and each part appearing in many assemblies, you could declare the models this way: 一个has_and_belongs_to_many关系创建一个直接的many-to-many连接到另一个模型,没有干扰模型。例如,如果你的应用程序包含assembliesparts,其中每个assembly拥有多个parts并且每个part发生在多个集会中,你可以以这样的方式声明模型: class Assembly < ActiveRecord::Base has_and_belongs_to_many :parts end   class Part < ActiveRecord::Base has_and_belongs_to_many :assemblies end  

2.7 Choosing Between belongs_to and has_one

If you want to set up a 1–1 relationship between two models, you’ll need to add belongs_to to one, and has_one to the other. How do you know which is which? 如果你想在两个模型间设定一个1-1的关系,你将需要添加belongs_to到一个(模型中),以及has_one到另一个(模型中)。你是怎么知道谁是谁的呢? The distinction区别is in where you place the foreign key (it goes on the table for the class declaring the belongs_to association), but you should give some thought to the actual meaning of the data as well. The has_one relationship says that one of something is yoursthat is, that something points back to you. For example, it makes more sense to say that a supplier owns an account than that an account owns a supplier. This suggests that the correct relationships are like this: class Supplier < ActiveRecord::Base has_one :account end   class Account < ActiveRecord::Base belongs_to :supplier end The corresponding migration might look like this: class CreateSuppliers < ActiveRecord::Migration def change create_table :suppliers do |t| t.string :name t.timestamps end   create_table :accounts do |t| t.integer :supplier_id t.string :account_number t.timestamps end end end Using t.integer :supplier_id makes the foreign key naming obvious and explicit. In current versions of Rails, you can abstract away this implementation detail by using t.references :supplier instead.使用t.integer :supplier_id使得外建命名明显和精确。在当前版本的Rails,你可以抽象掉这个实施细节通过使用t.references :supplier替代。

2.8 Choosing Between has_many :through and has_and_belongs_to_many

Rails offers two different ways to declare a many-to-many relationship between models. The simpler way is to use has_and_belongs_to_many, which allows you to make the association directly: Rails提供了两种不同的方式来声明一个模型间的many-to-many关系。比较简单的方式使用has_and_belongs_to_many,它允许你直接的(创建)关系: class Assembly < ActiveRecord::Base has_and_belongs_to_many :parts end   class Part < ActiveRecord::Base has_and_belongs_to_many :assemblies end The second way to declare a many-to-many relationship is to use has_many :through. This makes the association indirectly, through a join model: 第二种方式是声明一个many-to-many关系使用has_many :through,它可以使得关系间接的,同加入一个模型: class Assembly < ActiveRecord::Base has_many :manifests has_many :parts, :through => :manifests end   class Manifest < ActiveRecord::Base belongs_to :assembly belongs_to :part end   class Part < ActiveRecord::Base has_many :manifests has_many :assemblies, :through => :manifests end The simplest rule of thumb is that you should set up a has_many :through relationship if you need to work with the relationship model as an independent entity实体.If you don’t need to do anything with the relationship model, it may be simpler to set up a has_and_belongs_to_many relationship (though you’ll need to remember to create the joining table in the database).最简单的使用规则是,如果你需要使用关系模型作为依赖实体来工作你应该设定一个has_many :through关系You should use has_many:through if you need validations, callbacks, or extra attributes on the join model.

2.9 Polymorphic Associations多元关系

A slightly more advanced twist on associations is the polymorphic association.一个略微先进的关系枢纽是多元关系。With polymorphic associations, a model can belong to more than one other model, on a single association. 通过多态关系,(在单个关系中)一个模型可以属于超过一个其他的模型。For example, you might have a picture model that belongs to either an employee model or a product model. Here’s how this could be declared:例如,你可能有一个picture模型属于一个employee或者一个product模型。这是它们如何被定义的: class Picture < ActiveRecord::Base belongs_to :imageable, :polymorphic => true end   class Employee < ActiveRecord::Base has_many :pictures, :as => :imageable end   class Product < ActiveRecord::Base has_many :pictures, :as => :imageable end You can think of a polymorphic belongs_to declaration as setting up an interface that any other model can use. From an instance of the Employee model, you can retrieve a collection of pictures: @employee.pictures. 你可以认为一个多元belongs_to声明相当于设定一个其它任何模型可以使用的接口。来源于Employee模型的实例,你可以检索一个pictures的集合:@employee.pictures Similarly, you can retrieve @product.pictures.相近的,你可以检索@product.pictures If you have an instance of the Picture model, you can get to its parent via @picture.imageable. To make this work, you need to declare both a foreign key column and a type column in the model that declares the polymorphic interface:如果你有一个Picture模型的实例,你可以得到它的父模型通过@picture.imageable。要使这些工作,你需要声明一个外键字段和一个什么多元(关系)的接口的字段: class CreatePictures < ActiveRecord::Migration def change create_table :pictures do |t| t.string :name t.integer :imageable_id t.string :imageable_type t.timestamps end end end This migration can be simplified by using the t.references form:这个migration可以通过t.references形式简洁的(声明): class CreatePictures < ActiveRecord::Migration def change create_table :pictures do |t| t.string :name t.references :imageable, :polymorphic => true t.timestamps end end end

2.10 Self Joins

In designing a data model, you will sometimes find a model that should have a relation to itself. For example, you may want to store all employees in a single database model, but be able to trace relationships such as between manager and subordinates. This situation can be modeled with self-joining associations:在数据库设计中,有时你会发现一个模型具有一个(关联)到它自己的关系例如,你可能希望保存所有的employees在单个数据库模型中,但是能够跟踪关系比如在manager和subordinates之间。这个情况可以设定self-joining模型: class Employee < ActiveRecord::Base has_many :subordinates, :class_name => “Employee” belongs_to :manager, :class_name => “Employee”, :foreign_key => “manager_id” end With this setup, you can retrieve @employee.subordinates and @employee.manager.通过这个设定,你可以检索@employee.subordinates and @employee.manager

3 Tips提示, Tricks窍门, and Warnings警告

Here are a few things you should know to make efficient use of Active Record associations in your Rails applications:这里有一些事情你应该知道(它)使得在你的Rails应用程序使用Active Record关系变得高效:
  • Controllingcaching控制缓存
  • Avoidingnamecollisions避免名称碰撞
  • Updating the schema 更新结构
  • Controlling association scope 控制关系范围

3.1 Controlling Caching

All of the association methods are built around caching, which keeps the result of the most recent query available for further operations. The cache is even shared across methods. For example:所有的关系方法都创建在缓存周围,这使的最近的查询,可用于进一步的操作的结果。 customer.orders # retrieves orders from the database customer.orders.size # uses the cached copy of orders customer.orders.empty? # uses the cached copy of orders But what if you want to reload the cache, because data might have been changed by some other part of the application? Just pass true to the association call:但是如果你想重载cache,因为数据可能被应用程序的其他部分改变?仅仅通过添加投入额到关系调用: customer.orders # retrieves orders from the database customer.orders.size # uses the cached copy of orders customer.orders(true).empty? # discards the cached copy of orders # and goes back to the database

3.2 Avoiding Name Collisions避免名称碰撞

You are not free to use just any name for your associations. Because creating an association adds a method with that name to the model, it is a bad idea to give an association a name that is already used for an instance method of ActiveRecord::Base. The association method would override the base method and break things. For instance, attributes or connection are bad names for associations. 你不能不限制的给你的关系使用任何名字。因为创建一个关系添加一个方法和你命名的名字到模型,给一个关系取一个在ActiveRecord::Base实例方法中已经使用了的方法不是个好主意。这个关系方法将会覆盖原有方法和打断事情。比如实例,attributes or connection是关系的坏名称。

3.3 Updating the Schema

Associations are extremely useful, but they are not magic. You are responsible for maintaining your database schema to match your associations. In practice, this means two things, depending on what sort of associations you are creating. For belongs_to associations you need to create foreign keys, and for has_and_belongs_to_many associations you need to create the appropriate join table. 关系相当有用,但是它们不是魔法。你有责任注意你的数据库结构和你的关系的匹配。在实际中,这意味这两件事,依赖于你创建的何种关系。对于belongs_to关系你需要创建外键,对应has_and_belongs_to_many关系你需要创建适当的(关系)加入表中。
3.3.1 Creating Foreign Keys for belongs_to Associations
When you declare a belongs_to association, you need to create foreign keys as appropriate. For example, consider this model:当你声明一个belongs_to关系,你需要创建一个外键(作为合适的方式来更新数据结构)。例如,思考如下模型: class Order < ActiveRecord::Base belongs_to :customer end This declaration needs to be backed up by the proper foreign key declaration on the orders table:这个声明需要通过在生产者表单声明适当的外键备份。 class CreateOrders < ActiveRecord::Migration def change create_table :orders do |t| t.datetime :order_date t.string :order_number t.integer :customer_id end end end If you create an association some time after you build the underlying底层model, you need to remember to create an add_column migration to provide the necessary foreign key.有时你创建的关系在你创建的底层模型之后,你需要记住创建一个add_column migration提供必须的外键。    
3.3.2 Creating Join Tables for has_and_belongs_to_many Associations
If you create a has_and_belongs_to_many association, you need to explicitly create the joining table. Unless the name of the join table is explicitly specified by using the :join_table option, Active Record creates the name by using the lexical order of the class names. So a join between customer and order models will give the default join table name of “customers_orders” because “c” outranks “o” in lexical ordering. 如果你创建了一个has_and_belongs_to_many关系,你需要准确的创建joining表单。除非join表单使用:join_table选项准确的指定,(否则)Active Record就会使用类名提供的的词汇创建表单名称。因此一个在customerorder模型之间join(的表单)将会默认插入表名customers_orders因为co前面在提供的词汇中。 The precedence优先between model names is calculated using the < operator for String. This means that if the strings are of different lengths, and the strings are equal when compared up to the shortest length, then the longer string is considered of higher lexical precedence than the shorter one. For example, one would expect the tables “paper_boxes” and “papers” to generate a join table name of “papers_paper_boxes” because of the length of the name “paper_boxes”, but it in fact generates a join table name of “paper_boxes_papers” (because the underscore ‘_’ is lexicographically字典less than ‘s’ in common encodings). Whatever the name, you must manually手动generate the join table with an appropriate migration. For example, consider these associations: class Assembly < ActiveRecord::Base has_and_belongs_to_many :parts end   class Part < ActiveRecord::Base has_and_belongs_to_many :assemblies end These need to be backed up by a migration to create the assemblies_parts table. This table should be created without a primary key: class CreateAssemblyPartJoinTable < ActiveRecord::Migration def change create_table :assemblies_parts, :id => false do |t| t.integer :assembly_id t.integer :part_id end end end We pass :id => false to create_table because that table does not represent表示a model. That’s required for the association to work properly. If you observe观察any strange behavior in a has_and_belongs_to_many association like mangled错误models IDs, or exceptions about conflicting IDs chances are you forgot that bit.

3.4 Controlling Association Scope控制关系范围

By default, associations look for objects only within the current module’s scope. This can be important when you declare Active Record models within a module. For example:默认情况,(通过)关系查找对象仅仅在当前模型范围中。这很重要点你在一个module中声明Active Record models例如: module MyApplication module Business class Supplier < ActiveRecord::Base has_one :account end   class Account < ActiveRecord::Base belongs_to :supplier end end end This will work fine, because both the Supplier and the Account class are defined within the same scope. But the following will not work, because Supplier and Account are defined in different scopes: module MyApplication module Business class Supplier < ActiveRecord::Base has_one :account end end   module Billing class Account < ActiveRecord::Base belongs_to :supplier end end end To associate a model with a model in a different namespace, you must specify the complete class name in your association declaration: module MyApplication module Business class Supplier < ActiveRecord::Base has_one :account, :class_name => “MyApplication::Billing::Account” end end   module Billing class Account < ActiveRecord::Base belongs_to :supplier, :class_name => “MyApplication::Business::Supplier” end end end

4 Detailed Association Reference具体关系参考

The following sections give the details of each type of association, including the methods that they add and the options that you can use when declaring an association.接下来的部分给出了每种关系的具体的介绍,包含当你声明一个关系时可以使用的(这些关系)添加的方法和选项。
4.1.1 Methods Added by belongs_to
When you declare a belongs_to association, the declaring class automatically gains受益four methods related to the association:
  • association(force_reload = false)
  • association=(associate)
  • build_association(attributes = {})
  • create_association(attributes = {})
In all of these methods, association is replaced with the symbol passed as the first argument to belongs_to. For example, given the declaration: class Order < ActiveRecord::Base belongs_to :customer end Each instance of the order model生产者模型will have these methods: customer customer= build_customer create_customer   When initializing a new has_one or belongs_to association you must use the build_ prefix构造前缀to build the association, rather than the association. build method that would be used for has_many or has_and_belongs_to_many associations. To create one, use the create_ prefix新建前缀.
4.1.1.1 association(force_reload = false)
The association method returns the associated object, if any. If no associated object is found, it returns nil. @customer = @order.customer If the associated object has already been retrieved检索from the database for this object, the cached version will be returned. To override this behavior (and force a database read), pass true as the force_reload argument.
4.1.1.2 association=(associate)
The association= method assigns an associated object to this object. Behind the scenes, this means extracting the primary key from the associate object and setting this objects foreign key to the same value. @order.customer = @customer
4.1.1.3 build_association(attributes = {})
The build_association method returns a new object of the associated type. This object will be instantiated from the passed attributes, and the link through this object’s foreign key will be set, but the associated object will not yet be saved. @customer = @order.build_customer(:customer_number => 123, :customer_name => “John Doe”)
4.1.1.4 create_association(attributes = {})
The create_association method returns a new object of the associated type. This object will be instantiated from the passed attributes, and the link through this object’s foreign key will be set. In addition, the associated object will be saved (assuming that it passes any validations). @customer = @order.create_customer(:customer_number => 123, :customer_name => “John Doe”)
4.1.2 Options for belongs_to
In many situations, you can use the default behavior of belongs_to without any customization. But despite尽管Rails’ emphasis强调of convention公约over customization, you can alter that behavior in a number of ways. This section covers the options that you can pass when you create a belongs_to association. For example, an association with several options might look like this: class Order < ActiveRecord::Base belongs_to :customer, :counter_cache => true, :conditions => “active = 1” end The belongs_to association supports these options:
  • :autosave
  • :class_name
  • :conditions 条件
  • :counter_cache
  • :dependent
  • :foreign_key
  • :include
  • :polymorphic 多元
  • :readonly
  • :select
  • :touch
  • :validate
4.1.2.1 :autosave
If you set the :autosave option to true, Rails will save any loaded members and destroy members that are marked for destruction whenever you save the parent object.
4.1.2.2 :class_name
If the name of the other model cannot be derived from the association name, you can use the :class_name option to supply the model name. For example, if an order belongs to a customer, but the actual name of the model containing customers is Patron, you’d set things up this way:
4.1.2.3 :conditions
The :conditions option lets you specify the conditions that the associated object must meet (in the syntax used by an SQL WHERE clause). class Order < ActiveRecord::Base belongs_to :customer, :conditions => “active = 1” end
4.1.2.4 :counter_cache
The :counter_cache option can be used to make finding the number of belonging objects more efficient. Consider these models: class Order < ActiveRecord::Base belongs_to :customer end class Customer < ActiveRecord::Base has_many :orders end With these declarations, asking for the value of @customer.orders.size requires making a call to the database to perform a COUNT(*) query. To avoid this call, you can add a counter cache to the belonging model: class Order < ActiveRecord::Base belongs_to :customer, :counter_cache => true end class Customer < ActiveRecord::Base has_many :orders end With this declaration, Rails will keep the cache value up to date, and then return that value in response to the size method. Although the :counter_cache option is specified on the model that includes the belongs_to declaration, the actual column must be added to the associated model. In the case above, you would need to add a column named orders_count to the Customer model. You can override the default column name if you need to: class Order < ActiveRecord::Base belongs_to :customer, :counter_cache => :count_of_orders end class Customer < ActiveRecord::Base has_many :orders end Counter cache columns are added to the containing model’s list of read-only attributes through attr_readonly.
4.1.2.5 :dependent
If you set the :dependent option to :destroy, then deleting this object will call the destroy method on the associated object to delete that object. If you set the :dependent option to :delete, then deleting this object will delete the associated object without calling its destroy method. You should not specify this option on a belongs_to association that is connected with a has_many association on the other class. Doing so can lead to orphaned records in your database.对应多个的话用了dependent的话删除一个和他的外键的话其他关联就失效了
4.1.2.6 :foreign_key
By convention约定, Rails guesses that the column used to hold the foreign key on this model is the name of the association with the suffix _id added. The :foreign_key option lets you set the name of the foreign key directly: class Order < ActiveRecord::Base belongs_to :customer, :class_name => “Patron”, :foreign_key => “patron_id” end In any case, Rails will not create foreign key columns for you. You need to explicitly define them as part of your migrations.
4.1.2.7 :include
You can use the :include option to specify second-order associations that should be eager-loaded when this association is used. For example, consider these models: class LineItem < ActiveRecord::Base belongs_to :order end   class Order < ActiveRecord::Base belongs_to :customer has_many :line_items end   class Customer < ActiveRecord::Base has_many :orders end If you frequently retrieve customers directly from line items (@line_item.order.customer), then you can make your code somewhat more efficient by including customers in the association from line items to orders:如果你频繁的从line_item(@line_item.order.customer)直接检索,那么你可以在line_item关系中包含customers,使得你的代码变得更加有效率: class LineItem < ActiveRecord::Base belongs_to :order, :include => :customer end   class Order < ActiveRecord::Base belongs_to :customer has_many :line_items end   class Customer < ActiveRecord::Base has_many :orders end There’s no need to use :include for immediate立即(直接)associations – that is, if you have Order belongs_to :customer, then the customer is eager-loaded automatically when it’s needed.
4.1.2.8 :polymorphic
Passing true to the :polymorphic option indicates that this is a polymorphic association. Polymorphic associations were discussed in detail earlierinthisguide.
4.1.2.9 :readonly
If you set the :readonly option to true, then the associated object will be read-only when retrieved via the association.
4.1.2.10 :select
The :select option lets you override the SQL SELECT clause that is used to retrieve data about the associated object. By default, Rails retrieves all columns. If you set the :select option on a belongs_to association, you should also set the foreign_key option to guarantee保证the correct results.
4.1.2.11 :touch
If you set the :touch option to :true, then the updated_at or updated_on timestamp on the associated object will be set to the current time whenever this object is saved or destroyed: class Order < ActiveRecord::Base belongs_to :customer, :touch => true end   class Customer < ActiveRecord::Base has_many :orders end In this case, saving or destroying an order will update the timestamp on the associated customer. You can also specify a particular timestamp attribute to update: class Order < ActiveRecord::Base belongs_to :customer, :touch => :orders_updated_at end
4.1.2.12 :validate
If you set the :validate option to true, then associated objects will be validated whenever you save this object. By default, this is false: associated objects will not be validated when this object is saved.
4.1.3 How To Know Whether There’s an Associated Object?怎样知道这里是否有Associated 对象?
To know whether there’s and associated object just check association.nil?: if @order.customer.nil? @msg = “No customer found for this order” end
4.1.4 When are Objects Saved?
Assigning an object to a belongs_to association does not automatically save the object. It does not save the associated object either.

4.2 has_one Association Reference has_one关系参考

The has_one association creates a one-to-one match with another model. In database terms, this association says that the other class contains the foreign key. If this class contains the foreign key, then you should use belongs_to instead.
4.2.1 Methods Added by has_one
When you declare a has_one association, the declaring class automatically gains four methods related to the association:
  • association(force_reload = false)
  • association=(associate)
  • build_association(attributes = {})
  • create_association(attributes = {})
In all of these methods, association is replaced with the symbol passed as the first argument to has_one. For example, given the declaration: class Supplier < ActiveRecord::Base has_one :account end Each instance of the Supplier model will have these methods:每一个Supplier模型的实例将会具有如下方法: account account= build_account create_account When initializing a new has_one or belongs_to association you must use the build_ prefix to build the association, rather than the association.build method that would be used for has_many or has_and_belongs_to_many associations. To create one, use the create_ prefix.
4.2.1.1 association(force_reload = false)
The association method returns the associated object, if any. If no associated object is found, it returns nil. @account = @supplier.account If the associated object has already been retrieved from the database for this object, the cached version will be returned. To override this behavior (and force a database read), pass true as the force_reload argument.
4.2.1.2 association=(associate)
The association= method assigns an associated object to this object. Behind the scenes, this means extracting the primary key from this object and setting the associate object’s foreign key to the same value. @supplier.account = @account
4.2.1.3 build_association(attributes = {})
The build_association method returns a new object of the associated type. This object will be instantiated from the passed attributes, and the link through its foreign key will be set, but the associated object will notyetbesaved. @account = @supplier.build_account(:terms => “Net 30”)
4.2.1.4 create_association(attributes = {})
The create_association method returns a new object of the associated type. This object will be instantiated from the passed attributes, and the link through its foreign key will be set. In addition此外, the associated object willbesaved(assumingthatitpassesanyvalidations). @account = @supplier.create_account(:terms => “Net 30”)
4.2.2 Options for has_one
In many situations, you can use the default behavior of has_one without any customization. But despite Rails’ emphasis of convention over customization, you can alter that behavior in a number of ways. This section covers the options that you can pass when you create a has_one association. For example, an association with several options might look like this: class Supplier < ActiveRecord::Base has_one :account, :class_name => “Billing”, :dependent => :nullify end The has_one association supports these options:
  • :as
  • :autosave
  • :class_name
  • :conditions
  • :dependent
  • :foreign_key
  • :include
  • :order
  • :primary_key
  • :readonly
  • :select
  • :source
  • :source_type
  • :through
  • :validate
4.2.2.1 :as
Setting the :as option indicates that this is a polymorphic association. Polymorphic associations were discussed in detail earlierinthisguide.
4.2.2.2 :autosave
If you set the :autosave option to true, Rails will save any loaded members and destroy members that are marked for destruction whenever you save the parent object.
4.2.2.3 :class_name
If the name of the other model cannot be derived from the association name, you can use the :class_name option to supply the model name. For example, if a supplier has an account, but the actual name of the model containing accounts is Billing, you’d set things up this way: class Supplier < ActiveRecord::Base has_one :account, :class_name => “Billing” end
4.2.2.4 :conditions
The :conditions option lets you specify the conditions that the associated object must meet (in the syntax used by an SQL WHERE clause). class Supplier < ActiveRecord::Base has_one :account, :conditions => “confirmed = 1” end
4.2.2.5 :dependent
If you set the :dependent option to :destroy, then deleting this object will call the destroy method on the associated object to delete that object. If you set the :dependent option to :delete, then deleting this object will delete the associated object without calling its destroy method. If you set the :dependent option to :nullify, then deleting this object will set the foreign key in the association object to NULL.
4.2.2.6 :foreign_key
By convention, Rails guesses that the column used to hold the foreign key on the other model is the name of this model with the suffix _id added添加id后缀. The :foreign_key option lets you set the name of the foreign key directly: class Supplier < ActiveRecord::Base has_one :account, :foreign_key => “supp_id” end In any case, Rails will not create foreign key columns for you. You need to explicitly define them as part of your migrations.
4.2.2.7 :include
You can use the :include option to specify second-order associations that should be eager-loaded when this association is used. For example, consider these models: class Supplier < ActiveRecord::Base has_one :account end   class Account < ActiveRecord::Base belongs_to :supplier belongs_to :representative end   class Representative < ActiveRecord::Base has_many :accounts end If you frequently retrieve representatives directly from suppliers (@supplier.account.representative), then you can make your code somewhat more efficient by including representatives in the association from suppliers to accounts: class Supplier < ActiveRecord::Base has_one :account, :include => :representative end   class Account < ActiveRecord::Base belongs_to :supplier belongs_to :representative end   class Representative < ActiveRecord::Base has_many :accounts end
4.2.2.8 :order
The :order option dictates the order in which associated objects will be received (in the syntax used by an SQL ORDER BY clause). Because a has_one association will only retrieve a single associated object, this option should not be needed.
4.2.2.9 :primary_key
By convention, Rails guesses that the column used to hold the primary key of this model is id. You can override this and explicitly specify the primary key with the :primary_key option.
4.2.2.10 :readonly
If you set the :readonly option to true, then the associated object will be read-only when retrieved via the association.
4.2.2.11 :select
The :select option lets you override the SQL SELECT clause that is used to retrieve data about the associated object. By default, Rails retrieves all columns.
.2.2.12 :source
The :source option specifies the source association name for a has_one :through association.
4.2.2.13 :source_type
The :source_type option specifies the source association type for a has_one :through association that proceeds through a polymorphic association.
4.2.2.14 :through
The :through option specifies a join model through which to perform the query. has_one :through associations were discussed in detail earlierinthisguide.
4.2.2.15 :validate
If you set the :validate option to true, then associated objects will be validated whenever you save this object. By default, this is false: associated objects will not be validated when this object is saved.
4.2.3 How To Know Whether There’s an Associated Object?
To know whether there’s and associated object just check association.nil?: if @supplier.account.nil? @msg = “No account found for this supplier” end
4.2.4 When are Objects Saved?
When you assign an object to a has_one association, that object is automatically saved (in order to update its foreign key). In addition, any object being replaced is also automatically saved, because its foreign key will change too. If either of these saves fails due to validation errors, then the assignment statement returns false and the assignment itself is cancelled. If the parent object (the one declaring the has_one association) is unsaved (that is, new_record? returns true) then the child objects are not saved. They will automatically when the parent object is saved. If you want to assign an object to a has_one association without saving the object, use the association.build method.

4.3 has_many Association Reference

The has_many association creates a one-to-many relationship with another model. In database terms, this association says that the other class will have a foreign key that refers to instances of this class.
4.3.1 Methods Added by has_many
When you declare a has_many association, the declaring class automatically gains 13 methods related to the association: collection(force_reload = false)
  • collection<<(object,)
  • collection.delete(object,)
  • collection=objects
  • collection_singular_ids
  • collection_singular_ids=ids
  • collection.clear
  • collection.empty?
  • collection.size
  • collection.find()
  • collection.where()
  • collection.exists?()
  • collection.build(attributes = {},)
  • collection.create(attributes = {})
In all of these methods, collection is replaced with the symbol passed as the first argument to has_many, and collection_singular is replaced with the singularized version of that symbol.. For example, given the declaration: class Customer < ActiveRecord::Base has_many :orders end Each instance of the customer model will have these methods:   orders(force_reload = false) orders<<(object, …) orders.delete(object, …) orders=objects order_ids order_ids=ids orders.clear orders.empty? orders.size orders.find(…) orders.where(…) orders.exists?(…) orders.build(attributes = {}, …) orders.create(attributes = {})
4.3.1.1 collection(force_reload = false)
The collection method returns an array of all of the associated objects. If there are no associated objects, it returns an empty array. @orders = @customer.orders
4.3.1.2 collection<<(object,)
The collection<< method adds one or more objects to the collection by setting their foreign keys to the primary key of the calling model. @customer.orders << @order1
4.3.1.3 collection.delete(object,)
The collection.delete method removes one or more objects from the collection by setting their foreign keys to NULL. @customer.orders.delete(@order1) Additionally, objects will be destroyed if they’re associated with :dependent => :destroy, and deleted if they’re associated with :dependent => :delete_all.
4.3.1.4 collection=objects
The collection= method makes the collection contain only the supplied objects, by adding and deleting as appropriate.
4.3.1.5 collection_singular_ids
The collection_singular_ids method returns an array of the ids of the objects in the collection. @order_ids = @customer.order_ids
4.3.1.6 collection_singular_ids=ids
The collection_singular_ids= method makes the collection contain only the objects identified确定by the supplied primary key values, by adding and deleting as appropriate适当.
4.3.1.7 collection.clear
The collection.clear method removes every object from the collection. This destroys the associated objects if they are associated with :dependent => :destroy, deletes them directly from the database if :dependent => :delete_all, and otherwise sets their foreign keys to NULL.
4.3.1.8 collection.empty?
The collection.empty? method returns true if the collection does not contain any associated objects. <% if @customer.orders.empty? %> No Orders Found <% end %>
4.3.1.9 collection.size
The collection.size method returns the number of objects in the collection. @order_count = @customer.orders.size
4.3.1.10 collection.find()
The collection.find method finds objects within the collection. It uses the same syntax and options as ActiveRecord::Base.find. @open_orders = @customer.orders.where(:open => 1)
4.3.1.11 collection.where()
The collection.where method finds objects within the collection based on the conditions supplied but the objects are loaded lazily meaning that the database is queried only when the object(s) are accessed. @open_orders = @customer.orders.where(:open => true) # No query yet @open_order = @open_orders.first # Now the database will be queried
4.3.1.12 collection.exists?()
The collection.exists? method checks whether an object meeting the supplied conditions exists in the collection. It uses the same syntax and options as ActiveRecord::Base.exists?.
4.3.1.13 collection.build(attributes = {},)
The collection.build method returns one or more new objects of the associated type. These objects will be instantiated from the passed attributes, and the link through their foreign key will be created, but the associated objects will not yet be saved. @order = @customer.orders.build(:order_date => Time.now, :order_number => “A12345”)
4.3.1.14 collection.create(attributes = {})
The collection.create method returns a new object of the associated type. This object will be instantiated from the passed attributes, the link through its foreign key will be created, and the associated object will be saved (assuming that it passes any validations). @order = @customer.orders.create(:order_date => Time.now, :order_number => “A12345”)
4.3.2 Options for has_many
In many situations, you can use the default behavior for has_many without any customization. But you can alter that behavior in a number of ways. This section covers the options that you can pass when you create a has_many association. For example, an association with several options might look like this: class Customer < ActiveRecord::Base has_many :orders, :dependent => :delete_all, :validate => :false end The has_many association supports these options:
  • :as
  • :autosave
  • :class_name
  • :conditions
  • :counter_sql
  • :dependent
  • :extend
  • :finder_sql
  • :foreign_key
  • :group
  • :include
  • :limit
  • :offset
  • :order
  • :primary_key
  • :readonly
  • :select
  • :source
  • :source_type
  • :through
  • :uniq
  • :validate
4.3.2.1 :as
Setting the :as option indicates that this is a polymorphic association, as discussed earlierinthisguide.
4.3.2.2 :autosave
If you set the :autosave option to true, Rails will save any loaded members and destroy members that are marked for destruction whenever you save the parent object.
4.3.2.3 :class_name
If the name of the other model cannot be derived from the association name, you can use the :class_name option to supply the model name. For example, if a customer has many orders, but the actual name of the model containing orders is Transaction, you’d set things up this way: class Customer < ActiveRecord::Base has_many :orders, :class_name => “Transaction” end
4.3.2.4 :conditions
The :conditions option lets you specify the conditions that the associated object must meet (in the syntax used by an SQL WHERE clause). class Customer < ActiveRecord::Base has_many :confirmed_orders, :class_name => “Order”, :conditions => “confirmed = 1” end You can also set conditions via a hash: class Customer < ActiveRecord::Base has_many :confirmed_orders, :class_name => “Order”, :conditions => { :confirmed => true } end If you use a hash-style :conditions option, then record creation via this association will be automatically scoped using the hash. In this case, using @customer.confirmed_orders.create or @customer.confirmed_orders.build will create orders where the confirmed column has the value true. if you need to evaluate conditions dynamically at runtime, use a proc:
4.3.2.5 :counter_sql
Normally Rails automatically generates the proper SQL to count the association members. With the :counter_sql option, you can specify a complete SQL statement to count them yourself. If you specify :finder_sql but not :counter_sql, then the counter SQL will be generated by substituting SELECT COUNT(*) FROM for the SELECT FROM clause of your :finder_sql statement.
4.3.2.6 :dependent
If you set the :dependent option to :destroy(删除映射链和映射对象), then deleting this object will call the destroy method on the associated objects to delete those objects. If you set the :dependent option to :delete_all(只删除映射链), then deleting this object will delete the associated objects without calling their destroy method. If you set the :dependent option to :nullify, then deleting this object will set the foreign key in the associated objects to NULL. This option is ignored when you use the :through option on the association.
4.3.2.7 :extend
The :extend option specifies a named module to extend the association proxy. Association extensions are discussed in detail laterinthisguide.
4.3.2.8 :finder_sql
Normally Rails automatically generates the proper SQL to fetch the association members. With the :finder_sql option, you can specify a complete SQL statement to fetch them yourself. If fetching objects requires complex multi-table SQL, this may be necessary.
4.3.2.9 :foreign_key
By convention, Rails guesses that the column used to hold the foreign key on the other model is the name of this model with the suffix _id added添加前缀. The :foreign_key option lets you set the name of the foreign key directly: In any case, Rails will not create foreign key columns for you. You need to explicitly define them as part of your migrations.
4.3.2.10 :group
The :group option supplies an attribute name to group the result set by, using a GROUP BY clause in the finder SQL. class Customer < ActiveRecord::Base has_many :line_items, :through => :orders, :group => “orders.id” end
4.3.2.11 :include
You can use the :include option to specify second-order associations that should be eager-loaded when this association is used. For example, consider these models: class Customer < ActiveRecord::Base has_many :orders end   class Order < ActiveRecord::Base belongs_to :customer has_many :line_items end   class LineItem < ActiveRecord::Base belongs_to :order end If you frequently retrieve line items directly from customers (@customer.orders.line_items), then you can make your code somewhat more efficient by including line items in the association from customers to orders: class Customer < ActiveRecord::Base has_many :orders, :include => :line_items end   class Order < ActiveRecord::Base belongs_to :customer has_many :line_items end   class LineItem < ActiveRecord::Base belongs_to :order end
4.3.2.12 :limit
The :limit option lets you restrict the total number of objects that will be fetched through an association. class Customer < ActiveRecord::Base has_many :recent_orders, :class_name => “Order”, :order => “order_date DESC”, :limit => 100 end
4.3.2.13 :offset
The :offset option lets you specify the starting offset for fetching objects via an association. For example, if you set :offset => 11, it will skip the first 11 records.
4.3.2.14 :order
The :order option dictates the order in which associated objects will be received (in the syntax used by an SQL ORDER BY clause). class Customer < ActiveRecord::Base has_many :orders, :order => “date_confirmed DESC” end
4.3.2.15 :primary_key
By convention, Rails guesses that the column used to hold the primary key of the association is id. You can override this and explicitly specify the primary key with the :primary_key option.
4.3.2.16 :readonly
If you set the :readonly option to true, then the associated objects will be read-only when retrieved via the association.
4.3.2.17 :select
The :select option lets you override the SQL SELECT clause that is used to retrieve data about the associated objects. By default, Rails retrieves all columns. If you specify your own :select, be sure to include the primary key and foreign key columns of the associated model. If you do not, Rails will throw an error.
4.3.2.18 :source
The :source option specifies the source association name for a has_many :through association. You only need to use this option if the name of the source association cannot be automatically inferred from the association name.
4.3.2.19 :source_type
The :source_type option specifies the source association type for a has_many :through association that proceeds through a polymorphic association.
4.3.2.20 :through
The :through option specifies a join model through which to perform the query. has_many :through associations provide a way to implement many-to-many relationships, as discussed earlierinthisguide.
4.3.2.21 :uniq
Set the :uniq option to true to keep the collection free of duplicates. This is mostly useful together with the :through option. class Person < ActiveRecord::Base has_many :readings has_many :posts, :through => :readings end   person = Person.create(:name => ‘john’) post = Post.create(:name => ‘a1’) person.posts << post person.posts << post person.posts.inspect # => [#<Post id: 5, name: “a1”>, #<Post id: 5, name: “a1”>] Reading.all.inspect # => [#<Reading id: 12, person_id: 5, post_id: 5>, #<Reading id: 13, person_id: 5, post_id: 5>] In the above case there are two readings and person.posts brings out both of them even though these records are pointing to the same post. Now let’s set :uniq to true: class Person has_many :readings has_many :posts, :through => :readings, :uniq => true end   person = Person.create(:name => ‘honda’) post = Post.create(:name => ‘a1’) person.posts << post person.posts << post person.posts.inspect # => [#<Post id: 7, name: “a1”>] Reading.all.inspect # => [#<Reading id: 16, person_id: 7, post_id: 7>, #<Reading id: 17, person_id: 7, post_id: 7>] In the above case there are still two readings. However person.posts shows only one post because the collection loads only unique records.
4.3.2.22 :validate
If you set the :validate option to false, then associated objects will not be validated whenever you save this object. By default, this is true: associated objects will be validated when this object is saved.
4.3.3 When are Objects Saved?
When you assign an object to a has_many association, that object is automatically saved (in order to update its foreign key). If you assign multiple objects in one statement, then they are all saved. If any of these saves fails due to validation errors, then the assignment statement returns false and the assignment itself is cancelled. If the parent object (the one declaring the has_many association) is unsaved (that is, new_record? returns true) then the child objects are not saved when they are added. All unsaved members of the association will automatically be saved when the parent is saved. If you want to assign an object to a has_many association without saving the object, use the collection.build method.

4.4 has_and_belongs_to_many Association Reference

The has_and_belongs_to_many association creates a many-to-many relationship with another model. In database terms, this associates two classes via an intermediate join table that includes foreign keys referring to each of the classes.
4.4.1 Methods Added by has_and_belongs_to_many
When you declare a has_and_belongs_to_many association, the declaring class automatically gains 13 methods related to the association:
  • collection(force_reload = false)
  • collection<<(object,)
  • collection.delete(object,)
  • collection=objects
  • collection_singular_ids
  • collection_singular_ids=ids
  • collection.clear
  • collection.empty?
  • collection.size
  • collection.find()
  • collection.where()
  • collection.exists?()
  • collection.build(attributes = {})
  • collection.create(attributes = {})
In all of these methods, collection is replaced with the symbol passed as the first argument to has_and_belongs_to_many, and collection_singular is replaced with the singularized version of that symbol. For example, given the declaration: class Part < ActiveRecord::Base has_and_belongs_to_many :assemblies end Each instance of the part model will have these methods: assemblies(force_reload = false) assemblies<<(object, …) assemblies.delete(object, …) assemblies=objects assembly_ids assembly_ids=ids assemblies.clear assemblies.empty? assemblies.size assemblies.find(…) assemblies.where(…) assemblies.exists?(…) assemblies.build(attributes = {}, …) assemblies.create(attributes = {})
4.4.1.1 Additional Column Methods
If the join table for a has_and_belongs_to_many association has additional columns beyond the two foreign keys, these columns will be added as attributes to records retrieved via that association. Records returned with additional attributes will always be read-only, because Rails cannot save changes to those attributes. The use of extra attributes on the join table in a has_and_belongs_to_many association is deprecated. If you require this sort of complex behavior on the table that joins two models in a many-to-many relationship, you should use a has_many :through association instead of has_and_belongs_to_many.
4.4.1.2 collection(force_reload = false)
The collection method returns an array of all of the associated objects. If there are no associated objects, it returns an empty array. @assemblies = @part.assemblies
4.4.1.3 collection<<(object,)
The collection<< method adds one or more objects to the collection by creating records in the join table. @part.assemblies << @assembly1 This method is aliased as collection.concat and collection.push.
4.4.1.4 collection.delete(object,)
The collection.delete method removes one or more objects from the collection by deleting records in the join table. This does not destroy the objects. @part.assemblies.delete(@assembly1)
4.4.1.5 collection=objects
The collection= method makes the collection contain only the supplied objects, by adding and deleting as appropriate.
4.4.1.6 collection_singular_ids
The collection_singular_ids method returns an array of the ids of the objects in the collection. @assembly_ids = @part.assembly_ids
4.4.1.7 collection_singular_ids=ids
The collection_singular_ids= method makes the collection contain only the objects identified by the supplied primary key values, by adding and deleting as appropriate.
4.4.1.8 collection.clear
The collection.clear method removes every object from the collection by deleting the rows from the joining table. This does not destroy the associated objects.
4.4.1.9 collection.empty?
The collection.empty? method returns true if the collection does not contain any associated objects. <% if @part.assemblies.empty? %> This part is not used in any assemblies <% end %>
4.4.1.10 collection.size
The collection.size method returns the number of objects in the collection. @assembly_count = @part.assemblies.size
4.4.1.11 collection.find()
The collection.find method finds objects within the collection. It uses the same syntax and options as ActiveRecord::Base.find. It also adds the additional condition that the object must be in the collection. @new_assemblies = @part.assemblies.all( :conditions => [“created_at > ?”, 2.days.ago]) Starting Rails 3, supplying options to ActiveRecord::Base.find method is discouraged. Use collection.where instead when you need to pass conditions.
4.4.1.12 collection.where()
The collection.where method finds objects within the collection based on the conditions supplied but the objects are loaded lazily meaning that the database is queried only when the object(s) are accessed. It also adds the additional condition that the object must be in the collection. @new_assemblies = @part.assemblies.where(“created_at > ?”, 2.days.ago)
4.4.1.13 collection.exists?()
The collection.exists? method checks whether an object meeting the supplied conditions exists in the collection. It uses the same syntax and options as ActiveRecord::Base.exists?.
4.4.1.14 collection.build(attributes = {})
The collection.build method returns a new object of the associated type. This object will be instantiated from the passed attributes, and the link through the join table will be created, but the associated object will not yet be saved. @assembly = @part.assemblies.build( {:assembly_name => “Transmission housing”})
4.4.1.15 collection.create(attributes = {})
The collection.create method returns a new object of the associated type. This object will be instantiated from the passed attributes, the link through the join table will be created, and the associated object will be saved (assuming that it passes any validations). @assembly = @part.assemblies.create( {:assembly_name => “Transmission housing”})
4.4.2 Options for has_and_belongs_to_many
In many situations, you can use the default behavior for has_and_belongs_to_many without any customization. But you can alter that behavior in a number of ways. This section covers the options that you can pass when you create a has_and_belongs_to_many association. For example, an association with several options might look like this: class Parts < ActiveRecord::Base has_and_belongs_to_many :assemblies, :uniq => true, :read_only => true end The has_and_belongs_to_many association supports these options:
  • :association_foreign_key
  • :autosave
  • :class_name
  • :conditions
  • :counter_sql
  • :delete_sql
  • :extend
  • :finder_sql
  • :foreign_key
  • :group
  • :include
  • :insert_sql
  • :join_table
  • :limit
  • :offset
  • :order
  • :readonly
  • :select
  • :uniq
  • :validate
4.4.2.1 :association_foreign_key
By convention, Rails guesses that the column in the join table used to hold the foreign key pointing to the other model is the name of that model with the suffix _id added. The :association_foreign_key option lets you set the name of the foreign key directly: The :foreign_key and :association_foreign_key options are useful when setting up a many-to-many self-join. For example: class User < ActiveRecord::Base has_and_belongs_to_many :friends, :class_name => “User”, :foreign_key => “this_user_id”, :association_foreign_key => “other_user_id” end
4.4.2.2 :autosave
If you set the :autosave option to true, Rails will save any loaded members and destroy members that are marked for destruction whenever you save the parent object.
4.4.2.3 :class_name
If the name of the other model cannot be derived from the association name, you can use the :class_name option to supply the model name. For example, if a part has many assemblies, but the actual name of the model containing assemblies is Gadget, you’d set things up this way: class Parts < ActiveRecord::Base has_and_belongs_to_many :assemblies, :class_name => “Gadget” end
4.4.2.4 :conditions
The :conditions option lets you specify the conditions that the associated object must meet (in the syntax used by an SQL WHERE clause). class Parts < ActiveRecord::Base has_and_belongs_to_many :assemblies, :conditions => “factory = ‘Seattle’” end You can also set conditions via a hash: class Parts < ActiveRecord::Base has_and_belongs_to_many :assemblies, :conditions => { :factory => ‘Seattle’ } end If you use a hash-style :conditions option, then record creation via this association will be automatically scoped using the hash. In this case, using @parts.assemblies.create or @parts.assemblies.build will create orders where the factory column has the value “Seattle”.
4.4.2.5 :counter_sql
Normally Rails automatically generates the proper SQL to count the association members. With the :counter_sql option, you can specify a complete SQL statement to count them yourself. If you specify :finder_sql but not :counter_sql, then the counter SQL will be generated by substituting SELECT COUNT(*) FROM for the SELECT FROM clause of your :finder_sql statement.
4.4.2.6 :delete_sql
Normally Rails automatically generates the proper SQL to remove links between the associated classes. With the :delete_sql option, you can specify a complete SQL statement to delete them yourself.
4.4.2.7 :extend
The :extend option specifies a named module to extend the association proxy. Association extensions are discussed in detail laterinthisguide.
4.4.2.8 :finder_sql
Normally Rails automatically generates the proper SQL to fetch the association members. With the :finder_sql option, you can specify a complete SQL statement to fetch them yourself. If fetching objects requires complex multi-table SQL, this may be necessary.
4.4.2.9 :foreign_key
By convention, Rails guesses that the column in the join table used to hold the foreign key pointing to this model is the name of this model with the suffix _id added. The :foreign_key option lets you set the name of the foreign key directly: class User < ActiveRecord::Base has_and_belongs_to_many :friends, :class_name => “User”, :foreign_key => “this_user_id”, :association_foreign_key => “other_user_id” end
4.4.2.10 :group
The :group option supplies an attribute name to group the result set by, using a GROUP BY clause in the finder SQL. class Parts < ActiveRecord::Base has_and_belongs_to_many :assemblies, :group => “factory” end
4.4.2.11 :include
You can use the :include option to specify second-order associations that should be eager-loaded when this association is used.
4.4.2.12 :insert_sql
Normally Rails automatically generates the proper SQL to create links between the associated classes. With the :insert_sql option, you can specify a complete SQL statement to insert them yourself.
4.4.2.13 :join_table
If the default name of the join table, based on lexical ordering, is not what you want, you can use the :join_table option to override the default.
4.4.2.14 :limit
The :limit option lets you restrict the total number of objects that will be fetched through an association. class Parts < ActiveRecord::Base has_and_belongs_to_many :assemblies, :order => “created_at DESC”, :limit => 50 end
4.4.2.15 :offset
The :offset option lets you specify the starting offset for fetching objects via an association. For example, if you set :offset => 11, it will skip the first 11 records.
4.4.2.16 :order
The :order option dictates the order in which associated objects will be received (in the syntax used by an SQL ORDER BY clause). class Parts < ActiveRecord::Base has_and_belongs_to_many :assemblies, :order => “assembly_name ASC” end
4.4.2.17 :readonly
If you set the :readonly option to true, then the associated objects will be read-only when retrieved via the association.
4.4.2.18 :select
The :select option lets you override the SQL SELECT clause that is used to retrieve data about the associated objects. By default, Rails retrieves all columns.
4.4.2.19 :uniq
Specify the :uniq => true option to remove duplicates from the collection.
4.4.2.20 :validate
If you set the :validate option to false, then associated objects will not be validated whenever you save this object. By default, this is true: associated objects will be validated when this object is saved.
4.4.3 When are Objects Saved?
When you assign an object to a has_and_belongs_to_many association, that object is automatically saved (in order to update the join table). If you assign multiple objects in one statement, then they are all saved. If any of these saves fails due to validation errors, then the assignment statement returns false and the assignment itself is cancelled. If the parent object (the one declaring the has_and_belongs_to_many association) is unsaved (that is, new_record? returns true) then the child objects are not saved when they are added. All unsaved members of the association will automatically be saved when the parent is saved. If you want to assign an object to a has_and_belongs_to_many association without saving the object, use the collection.build method.

4.5 Association Callbacks

Normal callbacks hook into the life cycle of Active Record objects, allowing you to work with those objects at various points. For example, you can use a :before_save callback to cause something to happen just before an object is saved. Association callbacks are similar to normal callbacks, but they are triggered by events in the life cycle of a collection. There are four available association callbacks:
  • before_add
  • after_add
  • before_remove
  • after_remove
You define association callbacks by adding options to the association declaration. For example: class Customer < ActiveRecord::Base has_many :orders, :before_add => :check_credit_limit   def check_credit_limit(order) … end end You can stack callbacks on a single event by passing them as an array: class Customer < ActiveRecord::Base has_many :orders, :before_add => [:check_credit_limit, :calculate_shipping_charges]   def check_credit_limit(order) … end   def calculate_shipping_charges(order) … end end If a before_add callback throws an exception, the object does not get added to the collection. Similarly, if a before_remove callback throws an exception, the object does not get removed from the collection.

4.6 Association Extensions

You’re not limited to the functionality that Rails automatically builds into association proxy objects. You can also extend these objects through anonymous modules, adding new finders, creators, or other methods. For example: class Customer < ActiveRecord::Base has_many :orders do def find_by_order_prefix(order_number) find_by_region_id(order_number[0..2]) end end end If you have an extension that should be shared by many associations, you can use a named extension module. For example: module FindRecentExtension def find_recent where(“created_at > ?”, 5.days.ago) end end   class Customer < ActiveRecord::Base has_many :orders, :extend => FindRecentExtension end   class Supplier < ActiveRecord::Base has_many :deliveries, :extend => FindRecentExtension end To include more than one extension module in a single association, specify an array of modules: class Customer < ActiveRecord::Base has_many :orders, :extend => [FindRecentExtension, FindActiveExtension] end Extensions can refer to the internals of the association proxy using these three accessors访问器:
  • proxy_owner returns the object that the association is a part of.
  • proxy_reflection returns the reflection object that describes the association.
  • proxy_target returns the associated object for belongs_to or has_one, or the collection of associated objects for has_many or has_and_belongs_to_many.
  Now,supposewewantedtoaddaneworderforanexistingcustomer.We’dneedtodosomethinglikethis:现在假设我们想为一个存在的customer添加一个新的order。我们需要做如下事情:
标签: guide rails ruby

Active Record Validations and Callbacks 活动记录验证和回调

Active Record Validations and Callbacks 活动记录验证和回调

Active Record Validations and Callbacks 活动记录验证和回调

This guide teaches you how to hook勾子into the life cycle of your Active Record objects.这个教程指导你怎样挂接到你的Active Record objects的生存周期。You will learn how to validate the state of objects before they go into the database, and how to perform custom operations at certain points in the object life cycle.你将会学习到在将数据对象存入数据库之前怎样验证它们的状态,以及在对象生存周期的一些点上怎样执行定制操作。

After reading this guide and trying out the presented concepts, we hope that you’ll be able to:在阅读了这个教程以及尝试介绍的概念,我们希望你能够:

  • Understand the life cycle of Active Record objects 理解 Active Record对象的生存周期
  • Use the built-in Active Record validation helpers 使用内建的 Active Record验证helpers
  • Create your own custom validation methods 创建属于你的定制验证方法
  • Work with the error messages generated by the validation process 在验证过程中使用错误消息创建器工作
  • Create callback methods that respond to events in the object life cycle 新建一个回调方法响应对象生存周期的事件
  • Create special classes that encapsulate封装common behavior for your callbacks 创建特殊的类来封装你回调的通常习惯(方法)
  • Create Observers观察员that respond to life cycle events outside of the original class 创建 Observers来响应原类以外的生存周期事件

1 The Object Life Cycle

During the normal operation of a Rails application, objects may be created, updated, and destroyed. Active Record provides hooks into this object life cycle so that you can control your application and its data.一个Rails应用程序的正常操作期间,对象可能被新建,更新,和销毁。Active Record提供挂载到这个对象生存周期,以便你可以控制你的应用程序和数据。

Validations allow you to ensure that only valid data is stored in your database. Callbacks and observers allow you to trigger触发logic before or after an alteration改造变动of an object’s state.验证允许你确保只有验证数据被存储到你的数据库。回调和观察员(监视器)允许你在变动一个对象的状态之前或之后触发逻辑。

2 Validations Overview验证概述

Before you dive into the detail of validations in Rails, you should understand a bit about how validations fit into the big picture.在你深入Rails 验证的详细说明之前,你应该明白一点就是关于怎样让验证适合于(Rails)这幅大画卷。

2.1 Why Use Validations?

Validations are used to ensure that only valid data is saved into your database. For example, it may be important to your application to ensure that every user provides a valid email address and mailing address.验证用于确保只有通过验证的数据被保存入你的数据库。例如,在你的应用程序中确认每个用户提供了一个有效的Email地址和邮寄地址是非常重要的。

There are several ways to validate data before it is saved into your database, including native database constraints, client-side validations, controller-level validations, and model-level validations.在数据被存储到你的数据库中之前这里有几种方法来验证它,包括本地数据库约束,客户端验证,控制层级别的验证和模型层级别的验证。

Database constraints and/or stored procedures make the validation mechanisms database-dependent and can make testing and maintenance more difficult. However, if your database is used by other applications, it may be a good idea to use some constraints at the database level. Additionally, database-level validations can safely handle some things (such as uniqueness in heavily-used tables) that can be difficult to implement otherwise.

  • Client-side validations can be useful, but are generally unreliable if used alone. If they are implemented using JavaScript, they may be bypassed if JavaScript is turned off in the user’s browser. However, if combined with other techniques, client-side validation can be a convenient way to provide users with immediate feedback as they use your site. 客户端验证是很有用的,但是一般情况下单独使用是靠不住的。如果他们使用JavaScript来实施(验证),它们可能被绕过如果用户的浏览器中JavaScript被关闭的话。然而,如果联合其他的技术,客户端验证是一种方便的方法来提供用户即使反馈(验证信息)如果他们使用你的的站点。
  • Controller-level validations can be tempting to use, but often become unwieldy and difficult to test and maintain. Whenever possible, it’s a good idea to keepyourcontrollersskinny, as it will make your application a pleasure to work with in the long run. 控制层的验证的使用是诱人的,但是通常变得笨重和难以测试和维护。只要有可能,它是一个保持你的控制层苗条好主意,它也使得你的应用程序愉快的长时间工作。
  • Model-level validations are the best way to ensure that only valid data is saved into your database. They are database agnostic, cannot be bypassed by end users, and are convenient to test and maintain. Rails makes them easy to use, provides built-in helpers for common needs, and allows you to create your own validation methods as well. 模板层的验证是最好的方式来确保只有有效的数据被保存进了你的数据库。它们与数据库无关,不能被终端用户绕过,并且方便测试和维护。Rails通过提供(基于)常规需要的内建的helpers,使得它们容易使用,并且同样允许你新建属于你的验证方法。

2.2 When Does Validation Happen?验证在什么时候发生?

There are two kinds of Active Record objects: those that correspond to a row inside your database and those that do not. When you create a fresh object, for example using the new method, that object does not belong to the database yet. Once you call save uponthat object it will be saved into the appropriate database table. Active Record uses the new_record? instance method to determine whether an object is already in the database or not. Consider the following simple Active Record class:这里有两种Active Record对象:一些对应于你数据库中的一行以及一些不是。当你创建一个新鲜的对象,例如使用新的方法,这个对象还不属于数据库。一旦你调用save(函数)之后这个对象将会被适当的保存入数据表单。Active Record使用new_record?实例方法来决定一个对象是否已经被保存进了数据库。思考下面简单的Active Record类:

class Person < ActiveRecord::Base

end

We can see how it works by looking at some rails console output:我们通过观察一些rails的控制台输可以明白它是怎么工作的:

$rails console

>> p = Person.new(:name => “John Doe”)

=> #<Person id: nil, name: “John Doe”, created_at: nil, :updated_at: nil>

>> p.new_record?

=> true

>> p.save

=> true

>> p.new_record?

=> false

Creating and saving a new record will send an SQL INSERT operation to the database. Updating an existing record will send an SQL UPDATE operation instead. Validations are typically run before these commands are sent to the database. If any validations fail, the object will be marked as invalid and Active Record will not perform the INSERT or UPDATE operation. This helps to avoid storing an invalid object in the database. You can choose to have specific validations run when an object is created, saved, or updated.创建和保存一个新的记录将会发送一个SQL INSERT操作到数据库。更新一个存在的记录将会发送一个SQL UPDATE操作。验证通常在这些命令被发送到数据库之前运行。如果任何验证失败,这个对象将会标记为非法并且Active Record将不会执行INSERTUPDATE操作。这有助于避免存储一个非法的数据到数据库。你可以在对象被创建,保存或更新的时候选择指定的验证执行。

There are many ways to change the state of an object in the database. Some methods will trigger触发validations, but some will not. This means that it’s possible to save an object in the database in an invalid state if you aren’t careful.这里有很多方法来改变对象在数据库中的状态。一些方法将会触发验证,但是一些却不会。这里的意思是如果你不仔细的话就可能以一种非法的状态保存一个对象到数据库。

The following methods trigger validations, and will save the object to the database only if the object is valid:下面的方法触发验证,并且如果对象是合法的话将会保存对象到数据库:

  • create
  • create!
  • save
  • save!
  • update
  • update_attributes
  • update_attributes!

The bang versions (e.g. save!) raise an exception if the record is invalid. The non-bang versions don’t: save and update_attributes return false, create and update just return the objects.有感叹号的形式(例如save!)在记录是非法的时候会唤起一个异常。没有感叹号形式的就不会:saveupdate_attributes返回false,createupdate只是返回这个对象

2.3 Skipping Validations忽略验证

The following methods skip validations, and will save the object to the database regardless无论of its validity. They should be used with caution.下面的方法会略过验证,并且将会保存对象到数据库而无论它的有效性。他们应该慎重使用。

  • decrement! 递减
  • decrement_counter
  • increment!
  • increment_counter
  • toggle!
  • touch 切换
  • update_all
  • update_attribute
  • update_column
  • update_counters

Note that save also has the ability to skip validations if passed :validate => false as argument. This technique should be used with caution.注意save也可以略过演奏如果通过使用:validate => false作为参数。这个技术也应该慎重使用。

  • save(:validate => false)

2.4 valid? and invalid?

  1. To verify whether or not an object is valid, Rails uses the valid? method. You can also use this method on your own. valid? triggers your validations and returns true if no errors were added to the object, and false otherwise.验证一个对象是否有效,Rails使用valid?方法。你也可以使用属于你的方法。valid?触发你的验证并且返回True如果没有错误被添加到对象,否则就返回false

class Person < ActiveRecord::Base

validates :name, :presence => true

end

 

Person.create(:name => “John Doe”).valid? # => true

Person.create(:name => nil).valid? # => false

 

When Active Record is performing validations, any errors found can be accessed through the errors instance method. By definition an object is valid if this collection is empty after running validations.Active Record在执行验证的时候,任何发现的错误都可以通过errors实例方法来访问。通过定义一个对象是有效的如果这个集合在运行验证过后是空的。

Note that an object instantiated with new will not report errors even if it’s technically invalid, because validations are not run when using new.注意当实例化一个新的对象的时候将不会报告错误即使假设它的验证技术(得出数据)是非有效的,因为在使用new的时候验证是没有运行的。

class Person < ActiveRecord::Base

validates :name, :presence => true

end

 

>> p = Person.new

=> #<Person id: nil, name: nil>

>> p.errors

=> {}

 

>> p.valid?

=> false

>> p.errors

=> {:name=>[“can’t be blank”]}

 

>> p = Person.create

=> #<Person id: nil, name: nil>

>> p.errors

=> {:name=>[“can’t be blank”]}

 

>> p.save

=> false

 

>> p.save!

=> ActiveRecord::RecordInvalid: Validation failed: Name can’t be blank

 

>> Person.create!

=> ActiveRecord::RecordInvalid: Validation failed: Name can’t be blank

 

invalid? is simply the inverse of valid?. invalid? triggers your validations and returns true if any errors were added to the object, and false otherwise. invalid?valid?简单的逆。invalid?触发你的验证如果有任何错误添加到对象中则返回true,否则返回false

2.5 errors[]

To verify whether or not a particular attribute of an object is valid, you can use errors[:attribute]. It returns an array of all the errors for :attribute. If there are no errors on the specified attribute, an empty array is returned.验证一个对象具体的属性是否有效,你可以使用errors[:attribute]。它返回有关:attribute所有错误的数组。如果指定的属性没有错误,将会返回一个空数组。

This method is only useful after validations have been run, because it only inspects the errors collection and does not trigger validations itself. It’s different from the ActiveRecord::Base#invalid? method explained above because it doesn’t verify the validity of the object as a whole. It only checks to see whether there are errors found on an individual attribute of the object.这个方法只有在验证被执行过后才有用,因为它仅仅检查错误集合却不会自己触发验证。它和ActiveRecord::Base#invalid?方法上面解释的不同因为它不会整个验证对象的有效性。它仅仅检查这个对象的个别属性是否有错误被找到。

class Person < ActiveRecord::Base

validates :name, :presence => true

end

 

>> Person.new.errors[:name].any? # => false

>> Person.create.errors[:name].any? # => true

We’ll cover validation errors in greater depth in the WorkingwithValidationErrors section. For now, let’s turn to the built-in validation helpers that Rails provides by default.我们将会非常深入的涵盖验证错误在WorkingwithValidationErrors。现在,让我们转入Rails默认提供的内建的验证helpers

 

3 Validation Helpers

Active Record offers many pre-defined validation helpers that you can use directly inside your class definitions. These helpers provide common validation rules. Every time a validation fails, an error message is added to the object’s errors collection, and this message is associated with the field being validated.Active Record提供许多预定义的你可以插入你的类中直接使用的验证helpers。这些helpers提供常规验证规则。每次验证失败,一个错误消息会被添加到对象的errors集合中,并且这些消息和被验证的field相关。

Each helper accepts an arbitrary number of attribute names, so with a single line of code you can add the same kind of validation to several attributes.每个helper接受一个任意数目的属性名字,因此使用一行代码你可以给一些属性添加相同类型验证。

All of them accept the :on and :message options, which define when the validation should be run and what message should be added to the errors collection if it fails, respectively个别的. The :on option takes one of the values :save (the default), :create or :update. There is a default error message for each one of the validation helpers. These messages are used when the :message option isn’t specified. Let’s take a look at each one of the available helpers.所有的(helpers)接受:on:message选项,用来对个别的(helpers)定义什么时候运行验证以及如果验证失败什么消息被添加到errors集合。:on选项获取:save (默认的), :create:update的值。这里每个验证helpers是默认错误消息。这些消息在:message选项没有被指定时使用。下面我们来看看每个可用的helpers

3.1 acceptance接受承认

Validates that a checkbox on the user interface was checked when a form was submitted. This is typically used when the user needs to agree to your application’s terms of service, confirm reading some text, or any similar concept. This validation is very specific to web applications and this ‘acceptance’ does not need to be recorded anywhere in your database (if you don’t have a field for it, the helper will just create a virtual attribute).这个是很特殊对于web应用程序的验证并且这个acceptance’ 不需要在你的数据库中的任何地方记录(如果没有一个它的fieldhelper将会仅仅创建一个虚拟属性)。

class Person < ActiveRecord::Base

validates :terms_of_service, :acceptance => true

end

The default error message for this helper is must be accepted.

It can receive an :accept option, which determines the value that will be considered acceptance. It defaults to1and can be easily changed.它可以接收一个:accept选项,用来决定哪个值认为是接受。默认是1并且可以很容易更改成其他的。

class Person < ActiveRecord::Base

validates :terms_of_service, :acceptance => { :accept => ‘yes’ }

end

 

3.2 validates_associated

You should use this helper when your model has associations with other models and they also need to be validated. When you try to save your object, valid? will be called upon each one of the associated objects.

class Library < ActiveRecord::Base

has_many :books

validates_associated :books

end

This validation will work with all of the association types.

Dontusevalidates_associatedonbothendsofyourassociations.Theywouldcalleachotherinaninfinite无限loop.

The default error message for validates_associated is “is invalid”. Note that each associated object will contain its own errors collection; errors do not bubble up冒泡to the calling model.

 

3.3 confirmation确认

You should use this helper when you have two text fields that should receive exactly the same content. For example, you may want to confirm an email address or a password. This validation creates a virtual attribute whose name is the name of the field that has to be confirmed with “_confirmation” appended.

你应该使用这个helper当你有两个内容完全相同的文本框需要回收。(验证输入)

class Person < ActiveRecord::Base

validates :email, :confirmation => true

end

In your view template you could use something like

<%= text_field :person, :email %>

<%= text_field :person, :email_confirmation %>

 

This check is performed only if email_confirmation is not nil. To require confirmation, make sure to add a presence check for the confirmation attribute (we’ll take a look at presence later on this guide):

class Person < ActiveRecord::Base

validates :email, :confirmation => true

validates :email_confirmation, :presence => true

end

The default error message for this helper is “doesnt match confirmation”.

3.4 exclusion排除

This helper validates that the attributes’ values are not included in a given set. In fact, this set can be any enumerable列举object.

class Account < ActiveRecord::Base

validates :subdomain, :exclusion => { :in => %w(www us ca jp),

:message => “Subdomain %{value} is reserved.” }

end

The exclusion helper has an option :in that receives the set of values that will not be accepted for the validated attributes. The :in option has an alias called :within that you can use for the same purpose目的, if you’d like to. This example uses the :message option to show how you can include the attribute’s value.

The default error message is “is reserved”.

3.5 format格式(匹配)

This helper validates the attributes’ values by testing whether they match a given regular expression正则表达式, which is specified using the :with option.

class Product < ActiveRecord::Base

validates :legacy_code, :format => { :with => /\A[a-zA-Z]+\z/,

:message => “Only letters allowed” }

end

The default error message is “is invalid”.

3.6 inclusion列入

This helper validates that the attributes’ values are included in a given set. In fact, this set can be any enumerable object.

class Coffee < ActiveRecord::Base

validates :size, :inclusion => { :in => %w(small medium large),

:message => “%{value} is not a valid size” }

end

The inclusion helper has an option :in that receives the set of values that will be accepted. The :in option has an alias called :within that you can use for the same purpose, if you’d like to. The previous example uses the :message option to show how you can include the attribute’s value.

The default error message for this helper is “is not included in the list”.

3.7 length

This helper validates the length of the attributes’ values. It provides a variety of options, so you can specify length constraints in different ways:

class Person < ActiveRecord::Base

validates :name, :length => { :minimum => 2 }

validates :bio, :length => { :maximum => 500 }

validates :password, :length => { :in => 6..20 }

validates :registration_number, :length => { :is => 6 }

end

The possible length constraint约束options are:

  • :minimum – The attribute cannot have less than the specified length.
  • :maximum – The attribute cannot have more than the specified length.
  • :in (or :within) – The attribute length must be included in a given interval. The value for this option must be a range.
  • :is – The attribute length must be equal to the given value.

The default error messages depend on the type of length validation being performed. Youcanpersonalizethesemessagesusingthe:wrong_length,:too_long,and:too_shortoptionsand%{count}asaplaceholderforthenumbercorrespondingtothelengthconstraintbeingused.You can still use the :message option to specify an error message.

class Person < ActiveRecord::Base

validates :bio, :length => { :maximum => 1000,

:too_long => “%{count} characters is the maximum allowed” }

end

This helper counts characters by default, but you can split the value in a different way using the :tokenizer option:

class Essay < ActiveRecord::Base

validates :content, :length => {

:minimum => 300,

:maximum => 400,

:tokenizer => lambda { |str| str.scan(/\w+/) },

:too_short => “must have at least %{count} words”,

:too_long => “must have at most %{count} words”

}

end

Note that the default error messages are plural多元的(e.g., “is too short (minimum is %{count} characters)”). For this reason, when :minimum is 1 you should provide a personalized个性化message or use validates_presence_of instead. When :in or :within have a lower limit of 1, you should either provide a personalized message or call presence priorto length.(当长度比1还小就为空了应该使用validates_presence_of来验证

The size helper is an alias for length.

3.8 numericality只能输入数字

This helper validates that your attributes have only numeric values. By default, it will match an optional sign followed by an integral or floating point number. To specify that only integral numbers are allowed set :only_integer to true.

If you set :only_integer to true, then it will use the

/\A[+–]?\d+\Z/

regular expression to validate the attribute’s value. Otherwise, it will try to convert the value to a number using Float.

Notethattheregularexpressionaboveallowsatrailingnewlinecharacter.请注意,上面的正则表达式允许一个尾随的换行符

class Player < ActiveRecord::Base

validates :points, :numericality => true

validates :games_played, :numericality => { :only_integer => true }

end

Besides :only_integer, this helper also accepts the following options to add constraints to acceptable values:

  • :greater_than – Specifies the value must be greater than the supplied value. The default error message for this option is “must be greater than %{count}”. 大于
  • :greater_than_or_equal_to – Specifies the value must be greater than or equal to the supplied value. The default error message for this option is “must be greater than or equal to %{count}”.
  • :equal_to – Specifies the value must be equal to the supplied value. The default error message for this option is “must be equal to %{count}”.
  • :less_than – Specifies the value must be less than the supplied value. The default error message for this option is “must be less than %{count}”.
  • :less_than_or_equal_to – Specifies the value must be less than or equal the supplied value. The default error message for this option is “must be less than or equal to %{count}”.
  • :odd – Specifies the value must be an odd number if set to true. The default error message for this option is “must be odd”. 奇数
  • :even – Specifies the value must be an even number if set to true. The default error message for this option is “must be even”. 偶数

The default error message is “is not a number”.

3.9 presence存在

This helper validates that the specified attributes are not empty. It uses the blank? method to check if the value is either nil or a blank string, that is, a string that is either empty or consists of whitespace.

class Person < ActiveRecord::Base

validates :name, :login, :email, :presence => true

end

If you want to be sure that an association is present, you’ll need to test whether the foreign key used to map the association is present, and not the associated object itself.

class LineItem < ActiveRecord::Base

belongs_to :order

validates :order_id, :presence => true

end

Since false.blank? is true, if you want to validate the presence of a boolean field you should use validates :field_name, :inclusion => { :in => [true, false] }.

The default error message is “cant be empty”.

3.10 uniqueness独特性

This helper validates that the attribute’s value is unique right before the object gets saved. It does not create a uniqueness constraint in the database, so it may happen that two different database connections create two records with the same value for a column that you intend to be unique. To avoid that, you must create a unique index in your database.

class Account < ActiveRecord::Base

validates :email, :uniqueness => true

end

The validation happens by performing an SQL query into the model’s table, searching for an existing record with the same value in that attribute.

There is a :scope范围option that you can use to specify other attributes that are used to limit the uniqueness check:

class Holiday < ActiveRecord::Base

validates :name, :uniqueness => { :scope => :year,

:message => “should happen once per year” }

end

There is also a :case_sensitive option that you can use to define whether the uniqueness constraint约束will be case sensitive敏感or not. This option defaults to true.

class Person < ActiveRecord::Base

validates :name, :uniqueness => { :case_sensitive => false }

end

Note that some databases are configured to perform case-insensitive searches anyway.需要注意的是一些数据库配置为执行区分大小写的搜索。

The default error message is “has already been taken”.

3.11 validates_with通过指定类验证

This helper passes the record to a separate class for validation.

class Person < ActiveRecord::Base

validates_with GoodnessValidator

end

 

class GoodnessValidator < ActiveModel::Validator

def validate(record)

if record.first_name == “Evil”

record.errors[:base] << “This person is evil”

end

end

end

The validates_with helper takes a class, or a list of classes to use for validation. There is no default error message for validates_with. You must manually add errors to the record’s errors collection in the validator class.

To implement the validate method, you must have a record parameter defined, which is the record to be validated.

Like all other validations, validates_with takes the :if, :unless and :on options. If you pass any other options, it will send those options to the validator class as options:

class Person < ActiveRecord::Base

validates_with GoodnessValidator, :fields => [:first_name, :last_name]

end

 

class GoodnessValidator < ActiveModel::Validator

def validate(record)

if options[:fields].any?{|field| record.send(field) == “Evil” }

record.errors[:base] << “This person is evil”

end

end

end

3.12 validates_each

This helper validates attributes against a block. It doesn’t have a predefined validation function. You should create one using a block, and every attribute passed to validates_each will be tested against it. In the following example, we don’t want names and surnames to begin with lower case.

class Person < ActiveRecord::Base

validates_each :name, :surname do |model, attr, value|

model.errors.add(attr, ‘must start with upper case’) if value =~ /\A[a-z]/

end

end

The block receives the model, the attribute’s name and the attribute’s value. You can do anything you like to check for valid data within the block. If your validation fails, you can add an error message to the model, thereforemaking it invalid.

4 Common Validation Options常规验证选项

These are common validation options:

4.1 :allow_nil

The :allow_nil option skips the validation when the value being validated is nil.

class Coffee < ActiveRecord::Base

validates :size, :inclusion => { :in => %w(small medium large),

:message => “%{value} is not a valid size” }, :allow_nil => true

end

:allow_nilisignoredbythepresencevalidator.

4.2 :allow_blank

The :allow_blank option is similar to the :allow_nil option. This option will let validation pass if the attribute’s value is blank?, like nil or an empty string for example.

class Topic < ActiveRecord::Base

validates :title, :length => { :is => 5 }, :allow_blank => true

end

 

Topic.create(“title” => “”).valid? # => true

Topic.create(“title” => nil).valid? # => true

:allow_blankisignoredbythepresencevalidator.

4.3 :message

As you’ve already seen, the :message option lets you specify the message that will be added to the errors collection when validation fails. When this option is not used, Active Record will use the respective default error message for each validation helper.

4.4 :on

The :on option lets you specify when the validation should happen. The default behavior for all the built-in validation helpers is to be run on save (both when you’re creating a new record and when you’re updating it). If you want to change it, you can use :on => :create to run the validation only when a new record is created or :on => :update to run the validation only when a record is updated.:on 选项让你指定什么时候产生验证。

class Person < ActiveRecord::Base

it will be possible to update email with a duplicated value

validates :email, :uniqueness => true, :on => :create

 

it will be possible to create the record with a non-numerical age

validates :age, :numericality => true, :on => :update

 

the default (validates on both create and update)

validates :name, :presence => true, :on => :save

end

5 Conditional Validation有附加条件的验证

Sometimes it will make sense感觉to validate an object just when a given predicate is satisfied. You can do that by using the :if and :unless options, which can take a symbol, a string or a Proc. You may use the :if option when you want to specify when the validation should happen. If you want to specify when the validation should not happen, then you may use the :unless option.

有时候验证一个对象的时候如果给你一个判定性的(语句)你会感觉到满足。你可以使用:if:unless选项达到这样的效果,它可以(判断)一个符号(标记),字符串或者是Proc

 

Proc Ruby block的面向对象的封装。

 

5.1 Using a Symbol with :if and :unless

You can associate the :if and :unless options with a symbol corresponding to the name of a method that will get called right before validation happens. This is the most commonly used option.

class Order < ActiveRecord::Base

validates :card_number, :presence => true, :if => :paid_with_card?

 

def paid_with_card?

payment_type == “card”

end

end

5.2 Using a String with :if and :unless

YoucanalsouseastringthatwillbeevaluatedusingevalandneedstocontainvalidRubycode.You should use this option only when the string represents a really short condition.

class Person < ActiveRecord::Base

validates :surname, :presence => true, :if => “name.nil?”

end

5.3 Using a Proc with :if and :unless

Finally, it’s possible to associate :if and :unless with a Proc object which will be called. Using a Proc object gives you the ability to write an inline condition instead of a separate单独method. This option is best suited for one-liners.

class Account < ActiveRecord::Base

validates :password, :confirmation => true,

:unless => Proc.new { |a| a.password.blank? }

end

5.4 Grouping conditional validations

Sometimes it is useful to have multiple validations use one condition, it can be easily achieved using with_options.

class User < ActiveRecord::Base

with_options :if => :is_admin? do |admin|

admin.validates :password, :length => { :minimum => 10 }

admin.validates :email, :presence => true

end

end

All validations inside of with_options block will have automatically passed the condition :if => :is_admin?

6 Performing Custom Validations执行定制验证

When the built-in validation helpers are not enough for your needs, you can write your own validators or validation methods as you prefer.

6.1 Custom Validators定制验证器

Custom validators are classes that extend ActiveModel::Validator. These classes must implement a validate method which takes a record as an argument and performs the validation on it. The custom validator is called using the validates_with method.

class MyValidator < ActiveModel::Validator

def validate(record)

if record.name.starts_with? ‘X’

record.errors[:name] << ‘Need a name starting with X please!’

end

end

end

 

class Person

include ActiveModel::Validations

validates_with MyValidator

end

The easiest way to add custom validators for validating individual attributes is with the convenient ActiveModel::EachValidator. In this case, the custom validator class must implement实施落实a validate_each method which takes three arguments: record, attribute and value which correspond对应to the instance, the attribute to be validated and the value of the attribute in the passed instance.

最简单的方法添加(需要)验证个别的属性到定制的validatoras是便捷的使用ActiveModel::EachValidator。在这个案例中,定制的validator类必须落实validate_each方法获得三个参数:与实例对应的record, attribute and value,(它们是)接下来的实例的属性和值

class EmailValidator < ActiveModel::EachValidator

def validate_each(record, attribute, value)

unless value =~ /\A([^@\s]+)@((?:[-a-z0-9]+.)+[a-z]{2,})\z/i

record.errors[attribute] << (options[:message] || “is not an email”)

end

end

end

 

class Person < ActiveRecord::Base

validates :email, :presence => true, :email => true

end

As shown in the example, you can also combine结合standard validations with your own custom validators.

 

6.2 Custom Methods定制(验证)方法

You can also create methods that verify the state of your models and add messages to the errors collection when they are invalid. You must then register these methods by using one or more of the validate, validate_on_create or validate_on_update class methods, passing in the symbols符号for the validation methods’ names.

你也可以创建方法验证你models的状态以及在被验证的时候添加消息到errors集合。你必须注册这些方法通过使用一个或多个这些方法:validate, validate_on_create or validate_on_update类方法加上验证方法的名字。

You can pass more than one symbol for each class method and the respective各自的validations will be run in the same order as they were registered.你可以添加一个或多个符号给(对应的)每一个类方法并且各自的验证将会按照与注册相同的顺序运行。

class Invoice < ActiveRecord::Base

validate :expiration_date_cannot_be_in_the_past,

:discount_cannot_be_greater_than_total_value

 

def expiration_date_cannot_be_in_the_past

if !expiration_date.blank? and expiration_date < Date.today

errors.add(:expiration_date, “can’t be in the past”)

end

end

 

def discount_cannot_be_greater_than_total_value

if discount > total_value

errors.add(:discount, “can’t be greater than total value”)

end

end

end

Youcanevencreateyourownvalidationhelpersandreusetheminseveraldifferentmodels. For example, an application that manages surveys调查may find it useful to express that a certain某些field corresponds对应to a set of choices:

ActiveRecord::Base.class_eval do

def self.validates_as_choice(attr_name, n, options={})

validates attr_name, :inclusion => { {:in => 1..n}.merge(options) }

end

end

Simply reopen ActiveRecord::Base and define a class method like that. You’d typically put this code somewhere in config/initializers. You can use this helper like this:

class Movie < ActiveRecord::Base

validates_as_choice :rating, 5

end

7 Working with Validation Errors工作与Validation Errors

In addition to除了the valid? and invalid? methods covered earlier, Rails provides a number of methods for working with the errors collection and inquiring疑问about the validity of objects.

Thefollowingisalistofthemostcommonlyusedmethods.PleaserefertotheActiveRecord::Errorsdocumentationforalistofalltheavailablemethods.

7.1 errors

Returns an OrderedHash with all errors. Each key is the attribute name and the value is an array of strings with all errors.

class Person < ActiveRecord::Base

validates :name, :presence => true, :length => { :minimum => 3 }

end

 

person = Person.new

person.valid? # => false

person.errors

=> {:name => [“can’t be blank”, “is too short (minimum is 3 characters)”]}

 

person = Person.new(:name => “John Doe”)

person.valid? # => true

person.errors # => []

7.2 errors[]

errors[] is used when you want to check the error messages for a specific attribute. It returns an array of strings with all error messages for the given attribute, each string with one error message. If there are no errors related to the attribute, it returns an empty array.

class Person < ActiveRecord::Base

validates :name, :presence => true, :length => { :minimum => 3 }

end

 

person = Person.new(:name => “John Doe”)

person.valid? # => true

person.errors[:name] # => []

 

person = Person.new(:name => “JD”)

person.valid? # => false

person.errors[:name] # => [“is too short (minimum is 3 characters)”]

 

person = Person.new

person.valid? # => false

person.errors[:name]

=> [“can’t be blank”, “is too short (minimum is 3 characters)”]

7.3 errors.add

The add method lets you manually手动add messages that are related to particular attributes. You can use the errors.full_messages or errors.to_a methods to view the messages in the form they might be displayed to a user. Those particular messages get the attribute name prepended前置(and capitalized大写). add receives接收the name of the attribute you want to add the message to, and the message itself. #这里只是添加的消息并没有验证

class Person < ActiveRecord::Base

def a_method_used_for_validation_purposes

errors.add(:name, “cannot contain the characters !@#%*()_–+=”)

end

end

 

person = Person.create(:name => “!@#”)

 

person.errors[:name]

=> [“cannot contain the characters !@#%*()_–+=”]

 

person.errors.full_messages

=> [“Name cannot contain the characters !@#%*()_–+=”]

 

Another way to do this is using []= setter

class Person < ActiveRecord::Base

def a_method_used_for_validation_purposes

errors[:name] = “cannot contain the characters !@#%*()_–+=”

end

end

 

person = Person.create(:name => “!@#”)

 

person.errors[:name]

=> [“cannot contain the characters !@#%*()_–+=”]

 

person.errors.to_a

=> [“Name cannot contain the characters !@#%*()_–+=”]

7.4 errors[:base]

You can add error messages that are related to the object’s state as a whole, instead of being related to a specific attribute. You can use this method when you want to say that the object is invalid, no matter the values of its attributes. Since errors[:base] is an array, you can simply add a string to the array and uses it as the error message.

class Person < ActiveRecord::Base

def a_method_used_for_validation_purposes

errors[:base] << “This person is invalid because …”

end

end

7.5 errors.clear

The clear method is used when you intentionally有意want to clear all the messages in the errors collection. Of course, calling errors.clear upon之上an invalid object wont actually make it valid: the errors collection will now be empty, but the next time you call valid? or any method that tries to save this object to the database, the validations will run again. Ifanyofthevalidationsfail,theerrorscollectionwillbefilledagain.

class Person < ActiveRecord::Base

validates :name, :presence => true, :length => { :minimum => 3 }

end

 

person = Person.new

person.valid? # => false

person.errors[:name]

=> [“can’t be blank”, “is too short (minimum is 3 characters)”]

 

person.errors.clear

person.errors.empty? # => true

 

p.save # => false

 

p.errors[:name]

=> [“can’t be blank”, “is too short (minimum is 3 characters)”]

7.6 errors.size

The size method returns the total number of error messages for the object.

class Person < ActiveRecord::Base

validates :name, :presence => true, :length => { :minimum => 3 }

end

 

person = Person.new

person.valid? # => false

person.errors.size # => 3

 

person = Person.new(:name => “Andrea”, :email => “andrea@example.com”)

person.valid? # => true

person.errors.size # => 0

8 Displaying Validation Errors in the View在视图中显示验证错误(信息)

Rails maintains an official plugin that provides helpers to display the error messages of your models in your view templates. You can install it as a plugin or as a Gem.

8.1 Installing as a plugin

$ rails plugin install git://github.com/joelmoss/dynamic_form.git

8.2 Installing as a Gem

Add this line in your Gemfile:

gem “dynamic_form”

Now you will have access to these two methods in your view templates

8.3 error_messages and error_messages_for

When creating a form with the form_for helper, you can use the error_messages method on the form builder to render all failed validation messages for the current model instance.

class Product < ActiveRecord::Base

validates :description, :value, :presence => true

validates :value, :numericality => true, :allow_nil => true

end

<%= form_for(@product) do |f| %>

<%= f.error_messages %>

<p>

<%= f.label :description %><br />

<%= f.text_field :description %>

</p>

<p>

<%= f.label :value %><br />

<%= f.text_field :value %>

</p>

<p>

<%= f.submit “Create” %>

</p>

<% end %>

To get the idea, if you submit the form with empty fields you typically get this back, though styles are indeed missing by default:

You can also use the error_messages_for helper to display the error messages of a model assigned to a view template. It’s very similar to the previous example and will achieve exactly the same result.

<%= error_messages_for :product %>

The displayed text for each error message will always be formed by the capitalized name of the attribute that holds the error, followed by the error message itself.大写错误的属性名,后跟错误消息。

<%= f.error_messages :header_message => “Invalid product!”,

:message => “You’ll need to fix the following fields:”,

:header_tag => :h3 %>

Which results in the following content:

If you pass nil to any of these options, it will get rid of the respective section of the div.

8.4 Customizing the Error Messages CSS定制错误消息的CSS样式

The selectors to customize the style of error messages are:定制错误消息的选择器是:

  • field_with_errorsStyle for the form fields and labels with errors. Errorsform fieldslabels的样式
  • #errorExplanation – Style for the div element with the error messages. error消息的div元素样式
  • #errorExplanation h2 – Style for the header of the div element. div元素包含的标题样式
  • #errorExplanation p – Style for the paragraph that holds the message that appears right below the header of the div element.div元素中出现在标题下方段落的样式
  • #errorExplanation ul li – Style for the list items with individual error messages. 错误消息中个别的项目列表样式

Scaffolding脚手架for example generates app/assets/stylesheets/scaffold.css.scss, which later compiles to app/assets/stylesheets/scaffold.css and defines the red-based style you saw above.

The name of the class and the id can be changed with the :class and :id options, accepted by both helpers.

Scaffolding — 基架

  基于数据库架构生成网页模板的过程。在ASP .NET 中,动态数据使用基架来简化基于Web UI 的生成过程。用户可以通过这种UI 来查看和更新数据库。

8.5 Customizing the Error Messages HTML

By default, form fields with errors are displayed enclosed by a div element with the field_with_errors CSS class. However, it’s possible to override that.

The way form fields with errors are treated is defined by ActionView::Base.field_error_proc. This is a Proc that receives two parameters:

  • A string with the HTML tag
  • An instance of ActionView::Helpers::InstanceTag.

Here is a simple example where we change the Rails behavior to always display the error messages in front of each of the form fields with errors. The error messages will be enclosed by a span element with a validation-error CSS class. There will be no div element enclosing the input element, so we get rid of that red border around the text field. You can use the validation-error CSS class to style it anyway you want.

ActionView::Base.field_error_proc = Proc.new do |html_tag, instance|

if instance.error_message.kind_of?(Array)

%(#{html_tag}<span class=“validation-error”>&nbsp;

{instance.error_message.join(‘,’)}</span>).html_safe

else

%(#{html_tag}<span class=“validation-error”>&nbsp;

{instance.error_message}</span>).html_safe

end

end

This will result in something like the following:

9 Callbacks Overview回调概述

Callbacks are methods that get called at certain moments of an object’s life cycle. With callbacks it’s possible to write code that will run whenever an Active Record object is created, saved, updated, deleted, validated, or loaded from the database.

回调是一种在对象生存周期中的某些情况中(使得对象)被调用的方法。通过callbacks可以运行编写的代码无论Active Record对象是在数据库被创建,保存,删除,验证或者是导入。

9.1 Callback Registration

In order to use the available callbacks, you need to register them. You can do that by implementing实施them as ordinary一般methods, and then using a macro-style class method to register them as callbacks.

class User < ActiveRecord::Base

validates :login, :email, :presence => true

 

before_validation :ensure_login_has_a_value

 

protected

def ensure_login_has_a_value

if login.nil?

self.login = email unless email.blank?

end

end

end

The macro-style class methods can also receive接收a block. Consider using this style if the code inside your block is so short that it fits in just one line.

class User < ActiveRecord::Base

validates :login, :email, :presence => true

 

before_create do |user|

user.name = user.login.capitalize if user.name.blank?

end

end

It’s considered考虑good practice to declare callback methods as being protected or private. If left public, they can be called from outside of the model and violate违反the principle of object encapsulation封装.(基于)周密的考虑声明callback方法为protected或者private。如果是public,他们可以被model外调用这样违反了封装对象的原则。

10 Available Callbacks

Here is a list with all the available Active Record callbacks, listed in the same order in which they will get called during the respective operations:

10.1 Creating an Object

  • before_validation
  • after_validation
  • before_save
  • before_create
  • around_create
  • after_create
  • after_save

10.2 Updating an Object

  • before_validation
  • after_validation
  • before_save
  • before_update
  • around_update
  • after_update
  • after_save

10.3 Destroying an Object

  • before_destroy
  • after_destroy
  • around_destroy

after_save runs both on create and update, but always after the more specific callbacks after_create and after_update, no matter the order in which the macro calls were executed.after_save总是在after_create and after_update的后面不管他们的微调用如何。

10.4 after_initialize and after_find

The after_initialize callback will be called whenever an Active Record object is instantiated, either by directly using new or when a record is loaded from the database. It can be useful to avoid the need to directly override覆盖your Active Record initialize method.

 

The after_find callback will be called whenever Active Record loads a record from the database. after_find is called before after_initialize if both are defined.

 

 

The after_initialize and after_find callbacks have no before_* counterparts同行, but they can be registered just like the other Active Record callbacks.

 

class User < ActiveRecord::Base

after_initialize do |user|

puts “You have initialized an object!”

end

 

after_find do |user|

puts “You have found an object!”

end

end

 

>> User.new

You have initialized an object!

=> #<User id: nil>

 

>> User.first

You have found an object!

You have initialized an object!

=> #<User id: 1>

11 Running Callbacks

The following methods trigger callbacks:下面的方法触发回调:

  • create
  • create!
  • decrement! 递减
  • destroy
  • destroy_all
  • increment!
  • save
  • save!
  • save(false)
  • toggle!
  • update
  • update_attribute
  • update_attributes
  • update_attributes!
  • valid?

Additionally, the after_find callback is triggered by the following finder methods:此外,after_find会被下面的查找方法触发:

  • all
  • first
  • find
  • find_all_by_attribute
  • find_by_attribute
  • find_by_attribute!
  • last

The after_initialize callback is triggered every time a new object of the class is initialized.当类的一个新的对象被初始化的时候都会触发after_initialize回调。

12 Skipping Callbacks

Just as with validations, it’s also possible to skip callbacks. These methods should be used with caution, however, because important business rules and application logic may be kept in callbacks. Bypassing them without understanding the potential潜在implications影响may lead to invalid data.正如验证,它有可能忽略回调。这些方法需要注意使用,然而,由于重要的业务规则和应用程序逻辑可能保持回调。没有明白潜在的影响就绕过它们可能会导致非法数据。

  • decrement
  • decrement_counter
  • delete
  • delete_all
  • find_by_sql
  • increment
  • increment_counter
  • toggle
  • touch
  • update_column
  • update_all
  • update_counters

13 Halting Execution停止执行

As you start registering new callbacks for your models, they will be queued for execution. This queue will include all your model’s validations, the registered callbacks, and the database operation to be executed.

The whole callback chainis wrapped in a transaction交易. If any before callback method returns exactly false or raises an exception the execution chain gets halted and a ROLLBACK is issued; after callbacks can only accomplish完成that by raising an exception.

Raising an arbitrary exception may break code that expects save and friends not to fail like that.抛出任意异常可能会打断代码预计的save以及朋友们不想要的失败。The ActiveRecord::Rollback exception is thought precisely正是to tell Active Record a rollback is going on. That one is internally captured but not reraised.

14 Relational Callbacks Callbacks相关

Callbacks work through model relationships, and can even be defined by them. Let’s take an example where a user has many posts. In our example, a user’s posts should be destroyed if the user is destroyed. So, we’ll add an after_destroy callback to the User model by way of its relationship to the Post model.

class User < ActiveRecord::Base

has_many :posts, :dependent => :destroy

end

 

class Post < ActiveRecord::Base

after_destroy :log_destroy_action

 

def log_destroy_action

puts ‘Post destroyed’

end

end

 

>> user = User.first

=> #<User id: 1>

>> user.posts.create!

=> #<Post id: 1, user_id: 1>

>> user.destroy

Post destroyed

=> #<User id: 1>

15 Conditional Callbacks有条件的回调

Like in validations, we can also make our callbacks conditional, calling them only when a given predicate is satisfied. You can do that by using the :if and :unless options, which can take a symbol, a string or a Proc. You may use the :if option when you want to specify when the callback should get called. If you want to specify when the callback should not be called, then you may use the :unless option.

15.1 Using :if and :unless with a Symbol

You can associate the :if and :unless options with a symbol corresponding to the name of a method that will get called right before the callback. When using the :if option, the callback won’t be executed if the method returns false; when using the :unless option, the callback won’t be executed if the method returns true. This is the most common option. Using this form of registration it’s also possible to register several different methods that should be called to check if the callback should be executed.

class Order < ActiveRecord::Base

before_save :normalize_card_number, :if => :paid_with_card?

end

15.2 Using :if and :unless with a String

You can also use a string that will be evaluated using eval and needs to contain valid Ruby code. You should use this option only when the string represents a really short condition.

class Order < ActiveRecord::Base

before_save :normalize_card_number, :if => “paid_with_card?”

end

15.3 Using :if and :unless with a Proc

Finally, it’s possible to associate :if and :unless with a Proc object. This option is best suited when writing short validation methods, usually one-liners.

class Order < ActiveRecord::Base

before_save :normalize_card_number,

:if => Proc.new { |order| order.paid_with_card? }

end

15.4 Multiple Conditions for Callbacks

When writing conditional callbacks, it’s possible to mix both :if and :unless in the same callback declaration.

class Comment < ActiveRecord::Base

after_create :send_email_to_author, :if => :author_wants_emails?,

:unless => Proc.new { |comment| comment.post.ignore_comments? }

end

16 Callback Classes

Sometimes the callback methods that you’ll write will be useful enough to be reused by other models. Active Record makes it possible to create classes that encapsulate the callback methods, so it becomes very easy to reuse them.

Heres an example where we create a class with an after_destroy callback for a PictureFile model.

class PictureFileCallbacks

def after_destroy(picture_file)

if File.exists?(picture_file.filepath)

File.delete(picture_file.filepath)

end

end

end

When declared inside a class the callback method will receive the model object as a parameter. We can now use it this way:

class PictureFile < ActiveRecord::Base

after_destroy PictureFileCallbacks.new

end

Note that we needed to instantiate a new PictureFileCallbacks object, since we declared our callback as an instance method. Sometimes it will make more sense to have it as a class method.

class PictureFileCallbacks

def self.after_destroy(picture_file)

if File.exists?(picture_file.filepath)

File.delete(picture_file.filepath)

end

end

end

If the callback method is declared this way, it won’t be necessary to instantiate a PictureFileCallbacks object.

class PictureFile < ActiveRecord::Base

after_destroy PictureFileCallbacks

end

You can declare as many callbacks as you want inside your callback classes.

17 Observers观测者

Observers are similar to callbacks, but with important differences. Whereas callbacks can pollute a model with code that isn’t directly related to its purpose, observers allow you to add the same functionality outside of a model. For example, it could be argued that a User model should not include code to send registration confirmation emails. Whenever you use callbacks with code that isnt directly related to your model, you may want to consider creating an observer instead.

Observerscallbacks很相似,但是有着很大不同。鉴于callbacks能够影响model通过代码那与他的目的并没有直接关联,observers允许你在model外添加相同的功能。

17.1 Creating Observers

For example, imagine a User model where we want to send an email every time a new user is created. Because sending emails is not directly related to our model’s purpose, we could create an observer to contain this functionality.

$ rails generate observer User

 

class UserObserver < ActiveRecord::Observer

def after_create(model)

code to send confirmation email…

end

end

 

As with callback classes, the observer’s methods receive the observed model as a parameter.

17.2 Registering Observers

Observers are conventionally placed inside of your app/models directory and registered in your application’s config/application.rb file. For example, the UserObserver above would be saved as app/models/user_observer.rb and registered in config/application.rb this way:

# Activate observers that should always be running

config.active_record.observers = :user_observer

As usual, settings in config/environments take precedence优先权over those in config/application.rb. So, if you prefer that an observer doesn’t run in all environments, you can simply register it in a specific environment instead.

17.3 Sharing Observers

By default, Rails will simply strip “Observer” from an observer’s name to find the model it should observe. However, observers can also be used to add behavior to more than one model, and so it’s possible to manually specify the models that our observer should observe.

class MailerObserver < ActiveRecord::Observer

observe :registration, :user

 

def after_create(model)

code to send confirmation email…

end

end

In this example, the after_create method would be called whenever a Registration or User was created. Note that this new MailerObserver would also need to be registered in config/application.rb in order to take effect.

config.active_record.observers = :mailer_observer#与上面的类名相关

18 Transaction Callbacks

There are two additional callbacks that are triggered by the completion of a database transaction: after_commit and after_rollback. These callbacks are very similar to the after_save callback except that they don’t execute until after database changes have either been committed or rolled back. They are most useful when your active record models need to interact with external systems which are not part of the database transaction.

Consider, for example, the previous example where the PictureFile model needs to delete a file after a record is destroyed. If anything raises an exception after the after_destroy callback is called and the transaction rolls back, the file will have been deleted and the model will be left in an inconsistent state. For example, suppose that picture_file_2 in the code below is not valid and the save! method raises an error.

PictureFile.transaction do

picture_file_1.destroy

picture_file_2.save!

end

By using the after_commit callback we can account for this case.

class PictureFile < ActiveRecord::Base

attr_accessor :delete_file

 

after_destroy do |picture_file|

picture_file.delete_file = picture_file.filepath

end

 

after_commit do |picture_file|

if picture_file.delete_file && File.exist?(picture_file.delete_file)

File.delete(picture_file.delete_file)

picture_file.delete_file = nil

end

end

end

The after_commit and after_rollback callbacks are guaranteed to be called for all models created, updated, or destroyed within a transaction block. If any exceptions are raised within one of these callbacks, they will be ignored so that they don’t interfere with the other callbacks. As such, if your callback code could raise an exception, you’ll need to rescue it and handle it appropriately within the callback.

Markdown Syntax Guide

Markdown Syntax Guide

Markdown Syntax Guide The Allura code uses markdown syntax everywhere to allow you to create rich text markup, and extends markdown in several ways to allow for quick linking to other artifacts in your project.

Markdown was created to be easy to read, easy to write, and still readable in plain text format.

Links Reference Links Artifact Links Text Blockquotes Preformatted Text Lists Tables Headers Horizontal Rules Images Escapes More Headers Code Highlighting Includes Neighborhood Notifications Download Button Project Screenshots Thanks Links Most URLs will automatically be turned into links. To be explicit, just write it like this:

http://someurl

somebbob@example.com

Output:

http://someurl

somebbob@example.com

To use text for the link, write it:

like thisOutput:

like this

You can add a title (which shows up under the cursor):

like thisOutput:

like this

Reference Links You can also put the link URL below the current paragraph like this.

Output:

You can also put the link URL below the current paragraph like this.

Here the text “link URL” gets linked to “http://url”, and the lines showing “1: http://url” won’t show anything.

Or you can use a shortcut reference, which links the text “shortcut” to the link named “shortcut” on the next paragraph.

Or you can use a shortcut reference, which links the text “shortcut” to the link named “shortcut” on the next paragraph.

Or you can use a shortcut reference, which links the text “shortcut” to the link named “shortcut” on the next paragraph.

Artifact Links Any existing forge resource can be linked with surrounding square brackets ie [MyPage] or [#123].

Links to resources in other tools can be explicitly referenced by adding a tool identifier prefix to the link. So for instance [developerwiki:MyPage] can refer to a wiki page in a developerwiki instance. You can also link to tickets with [tickets:#123] assuming there’s a 123 ticket in a Tracker instance mounted at tickets. The same is true for forums, or any of the other tools you have installed. You can even link to tickets in a subproject with [subproject.tickets:#123].

[MyPage] [developerwiki:MyPage] [#123] [tickets:#123] No example output is available for this one because it only works on real artifacts. Try it in your project!

Text Use * or _ to emphasize things:

this is in italic and so is this

this is in bold and so is this

this is bold and italic and so is this Output:

this is in italic and so is this

this is in bold and so is this

this is bold and italic and so is this

You can strike through text using HTML like this:

this is strike through text Output:

this is strike through text

Just write paragraphs like in a text file and they will display how you would expect. A blank line separates paragraphs.

So this is a new paragraph. But any text on adjacent lines will all end up in the same paragraph. Output:

Just write paragraphs like in a text file and they will display how you would expect. A blank line separates paragraphs.

So this is a new paragraph. But any text on adjacent lines will all end up in the same paragraph.

Blockquotes Use the > character in front of a line, just like in email

Use it if you’re quoting a person, a song or whatever.

You can use italic or lists inside them also. And just like with other paragraphs, all of these lines are still part of the blockquote, even without the > character in front.

To end the blockquote, just put a blank line before the following paragraph. Output:

Use it if you’re quoting a person, a song or whatever.

You can use italic or lists inside them also. And just like with other paragraphs, all of these lines are still part of the blockquote, even without the > character in front.

To end the blockquote, just put a blank line before the following paragraph.

Preformatted Text If you want some text to show up exactly as you write it, without Markdown doing anything to it, just indent every line by at least 4 spaces (or 1 tab).

This line won't *have any markdown* formatting applied.
I can even write <b>HTML</b> and it will show up as text.
This is great for showing program source code, or HTML or even
Markdown. <b>this won't show up as HTML</b> but
exactly <i>as you see it in this text file</i>.

As a shortcut you can use backquotes to do the same thing while inside a normal pargraph. This won't be *italic* or **bold** at all. Output:

This line won’t have any markdown formatting applied. I can even write HTML and it will show up as text. This is great for showing program source code, or HTML or even Markdown. this won’t show up as HTML but exactly as you see it in this text file. As a shortcut you can use backquotes to do the same thing while inside a normal pargraph. This won’t be italic or bold at all.

Lists * an asterisk starts an unordered list * and this is another item in the list + or you can also use the + character – or the – character

To start an ordered list, write this:

  1. this starts a list with numbers
  2. this will show as number “2”
  3. this will show as number “3.”
  4. any number, +, –, or * will keep the list going.
    • just indent by 4 spaces (or tab) to make a sub-list
      1. keep indenting for more sub lists
    • here i’m back to the second level Output:

an asterisk starts an unordered list and this is another item in the list or you can also use the + character or the – character To start an ordered list, write this:

this starts a list with numbers this will show as number “2” this will show as number “3.” any number, +, –, or * will keep the list going. just indent by 4 spaces (or tab) to make a sub-list keep indenting for more sub lists here i’m back to the second level Tables You can create tables using pipes and dashes like this:

First Header Second Header
Content Cell Content Cell
Content Cell Content Cell

Output:

First Header Second Header Content Cell Content Cell Content Cell Content Cell

You can use markdown syntax within table cells for formatting:

First Header Second Header
Content Cell Content Cell
Content Cell Content Cell

Output:

First Header Second Header Content Cell Content Cell Content Cell Content Cell

You can also create tables using HTML code.

Headers Just put 1 or more dashes or equals signs (—– or ===) below the title.

This is a huge header

this is a smaller header

Output:

This is a huge header this is a smaller header Horizontal Rule Just put three or more *’s or –’s on a line:


Output:


Or, you can use single spaces between then, like this:


Output:


or


Output:


Make sure you have a blank line above the dashes, though, or else:

you will get a header

Output:

you will get a header Images To include an image, just put a “!” in front of a text link:

alternate text Output:

The “alternate text” will show up if the browser can’t load the image.

You can also use a title if you want, like this:

tiny arrow Output:

To reference an attached image, just use the img macro. You can add more attributes:

[[img src=attached-image.jpg alt=foobar]] Output:

Escapes What if you want to just show asterisks, not italics?

  • this shows up in italics: a happy day
  • this shows the asterisks: *a happy day* Output:

this shows up in italics: a happy day this shows the asterisks: a happy day The backslashes will disappear and leave the asterisks.

You can do the same with any of the characters that have a special meaning for Markdown.

HTML tags may need to be escaped. will be interpreted as a bold tag. Entity codes will be used. isn’t allowed and will be dropped, so you probably want to escape it:

this will be bold you should escape <unknown> tags < special entities work &lt; if you want to escape it Output:

this will be bold you should escape tags < special entities work < if you want to escape it

Individual ampersands (&) and less-than signs (<) are fine, they will be shown as expected.

More Headers More ways of doing headers:

this is a huge header

this is a smaller header

this is even smaller

more small

even smaller
smallest still: <h6> header

Output:

this is a huge header this is a smaller header this is even smaller more small even smaller smallest still:

header You can use up to 6 # characters at the beginning of the line.

Code Highlighting The Code highlighting used in the newforge is based on (http://www.freewisdom.org/projects/python-markdown/CodeHilite). It follows the same syntax as regular Markdown code blocks, except that there are two ways to tell the highlighter what language to use for the code block.

If the first line of the codeblock contains a shebang, the language is derived from that and line numbers are used.

#!/usr/bin/python
# Code goes here ...

Output:

1 2 #!/usr/bin/python

Code goes here …

If the first line contains a shebang, but the shebang line does not contain a path (a single / or even a space) or If the first line begins with three or more colons, the text following the colons identifies the language. In both cases, the first line is removed from the code block before processing.

:::python
# Code goes here ...

Output:

Code goes here …

You can also designate a code block by surrounding it with lines of tildes. The type of code highlighting to apply will be inferred based on the code within, or you can specify like above.

My code Output:

My code Includes You can embed another wiki page directly:

[[include ref=SamplePage]] No example output is available for this one because it only works on real wiki pages. Try it in your wiki!

Neighborhood Notifications You can list updates from all projects in a neighborhood by tool type. Max_number (default is 5) and sort (default is pubdate) are optional:

[[neighborhood_feeds tool_name=Wiki max_number=10 sort=pubdate]] Neighborhood Blog Posts You can view blog posts from all projects in a neighborhood. Max_number (default is 5) and sort (default is timestamp) are optional:

[[neighborhood_blog_posts max_number=10 sort=timestamp]] Project Blog Posts You can view blog posts from all blogs in a project. Max_number (default is 5), mount point (leave empty to view posts from all blog tools in a project), and sort (default is timestamp) are optional:

[[project_blog_posts max_number=10 sort=timestamp mount_point=news]] Download Button You can display a download button that links to the best download available for the active project. Please note that if you use this macro and there is no download associated with your project, the button will not appear.

[[download_button]] Project Screenshots You can show all the screenshots for the current project as thumbnails that are linked to the full-size image.

[[project_screenshots]] Thanks Thanks to John Gruber and Aaron Swartz for creating Markdown.

This page is based on some examples from Greg Schueler, greg@vario.us

Guides Rubyonrails Migrations (en and CN)

guides rubyonrails Migrations (EN and CN)

Migrations

Migrations are a convenient way for you to alter移动your database in a structured and organized manner.Migrations是一种很便捷的方法让你能够以一种结构化的和有组织的方式来迁移你的数据库。You could edit fragments of SQL by hand but you would then be responsible for telling other developers that they need to go and run them.你可以手动编辑SQL片段,而且你有责任把这些告诉其他的开发人员,因为他们需要开发和使用它们。You’d also have to keep track of which changes need to be run against the production machines next time you deploy.你也可以跟踪对你部署的代码在接下来的production机器(将会)发生的变化。

Active Record tracks which migrations have already been run so all you have to do is update your source and run rake db:migrate.Active Record跟踪并迁移你已经运行过的(代码和数据),而你只需要在更新了你的源代码的时候执行rake db:migrateActive Record will work out which migrations should be run.Active Recor将会计算出那些迁移需要被执行。It will also update your db/schema.rb file to match the structure of your database.它还会更新你的db/schema.rb文件使其于你的数据库结构相匹配。

Migrations also allow you to describe these transformations using Ruby.Migrations同样允许你使用Ruby来描述这些转换。The great thing about this is that (like most of Active Records functionality) it is database independent: you dont need to worry about the precise syntax of CREATE TABLE any more than you worry about variations on SELECT (you can drop down to raw SQL for database specific features).值得高兴的事情是(就像大多数的Active Record’s工厂)它是与数据独立的:你再也不需要担心准确的语法来CREATE TABLE也不需要担心SELECT 的变化。For example you could use SQLite3 in development, but MySQL in production.例如你可以使用SQLite3开发,但是在发布的产品中使用MySQL

Youll learn all about migrations including:下面你将了解到的migrations包括:

  • The generators you can use to create them 你可以使用generators来创建他们(数据库表单)
  • The methods Active Record provides to manipulate操纵your database Active Record提供方法来操纵你的数据库
  • The Rake tasks that manipulate them 使用Rake命令操作这些(迁移)
  • How they relate to schema.rb 它们是如何映射到schema.rb

1 Anatomy of a Migration

Before we dive into the details of a migration, here are a few examples of the sorts of things you can do:在深入migration的详细介绍之前,下面有一系列的例子你可以尝试一下:

class CreateProducts < ActiveRecord::Migration

def up

create_table :products do |t|

t.string :name

t.text :description

 

t.timestamps

end

end

 

def down

drop_table :products

end

end

This migration adds a table called products with a string column called name and a text column called description.这次migration添加了一个名叫products的表它有一个叫name的字符串的列和一个叫description的文本框的列。A primary key column called id will also be added, however since this is the default we do not need to ask for this.一个名叫id的主键列也被添加,然而因为这是默认操作的不需要我们刻意添加。The timestamp columns created_at and updated_at which Active Record populates automatically will also be added.还添加了timestamp字段,products表单会在created_atupdated_at的时候通过Active Record自动填充timestamp字段。Reversing this migration is as simple as dropping the table.撤销这次migration就像dropping这个表。

 

Migrations are not limited to changing the schema.Migrations不限制更改schemaYou can also use them to fix bad data in the database or populate new fields:你可以使用(schema)它们来修复坏的数据或者添加新的字段:

class AddReceiveNewsletterToUsers < ActiveRecord::Migration

def up

change_table :users do |t|

t.boolean :receive_newsletter, :default => false

end

User.update_all [“receive_newsletter = ?”, true]

end

 

def down

remove_column :users, :receive_newsletter

end

end

Some caveats apply to using models in your migrations.一些在modelmigrations中的注意事项

This migration adds a receive_newsletter column to the users table.(上面)这个migration添加一个receive_newsletter字段到user表。We want it to default to false for new users, but existing users are considered to have already opted in, so we use the User model to set the flag to true for existing users.我们希望对于新用户默认设置receive_newsletter字段为fasle,但是存在的用户被认为已经有(自己的)选择,因此我们通过在User model中存在的用户设置为True的标识(来保留以后信息)。

Rails 3.1 makes migrations smarter by providing a new change method.Rails3.1通过提供一个新的change方法,使得migrations更加智能化。This method is preferred首选for writing constructive migrations (adding columns or tables).这个方法是用来做(数据库)结构迁移(添加或删除字段)的首选。The migration knows how to migrate your database and reverse it when the migration is rolled back without the need to write a separate down method.migration知道怎样迁移你的数据库以及不需要单独的编写down方法来处理回滚是的migration

class CreateProducts < ActiveRecord::Migration

def change

create_table :products do |t|

t.string :name

t.text :description

 

t.timestamps

end

end

end

1.1 Migrations are Classes Migrations是一个类

A migration is a subclass of ActiveRecord::Migration that implements two methods: up (perform the required transformations) and down (revert them).一个migration类是ActiveRecord::Migration的子类,它实现了两个方法:up(执行所请求的转换)和down(撤销所做的更改)。

Active Record provides methods that perform common data definition tasks in a database independent way (you’ll read about them in detail later):Active Record提供了了在数据库中执行常见数据定义的方法(你将会在后面看到详细的介绍)。

  • create_table
  • change_table
  • drop_table
  • add_column
  • change_column
  • rename_column
  • remove_column
  • add_index
  • remove_index

If you need to perform tasks specific to your database (for example create a foreignkey constraint约束) then the execute function allows you to execute arbitrary SQL.如果你在你的数据库中需要处理特殊的任务(例如创建一个foreign key约束)那么execute功能允许你执行任意的SQL(语句)。A migration is just a regular Ruby class so you’re not limited to these functions. migration仅仅是一个Ruby类,因此你不必仅仅局限于现有的这些功能。For example after adding a column you could write code to set the value of that column for existing records (if necessary using your models).例如在添加了一个字段之后你可以添加代码来设置这个字段在存在记录中的值(如果在你的model中需要)。

On databases that support transactions处理办理with statements that change the schema (such as PostgreSQL or SQLite3), migrations are wrapped in a transaction.当数据库支持通过声明来改变数据库的结构(例如PostgreSQLSQLite3),会在包含在migration处理中(直接改变数据库的结构)。If the database does not support this (for example MySQL) then when a migration fails the parts of it that succeeded will not be rolled back. 如果数据库不支持这样的功能(比如MySQL)然后migration会有部分失败——成功添加的(数据)将不会回滚。You will have to unpick the changes that were made by hand.你必须手动的分开这些改变。

1.2 What’s in a Name (数据库文件)名称中的信息

Migrations are stored in files in db/migrate, one for each migration class.Mgirations以单个文件的形式被存放在文件夹db/migrateThe name of the file is of the form YYYYMMDDHHMMSS_create_products.rb, that is to say a UTC timestamp时间戳identifying确定the migration followed by an underscore下划线followed by the name of the migration.migration)文件是以form命名的,还看见了一个在migration完成之时的时间戳接着是下划线接着是migration的名字。The name of the migration class (CamelCased version) should match the latter part of the file name. migraiton类(使用驼峰命名法)的名字应该和migration文件的名称的最后部分相匹配。For example 20080906120000_create_products.rb should define CreateProducts and 20080906120001_add_details_to_products.rb should define AddDetailsToProducts. If you do feel the need to change the file name then you have to update the name of the class inside or Rails will complain about a missing class.如果你觉得需要改变migration文件的名字你必须同样修改文件里边migration类的名字,不然Rails会找不到migration类。

Internally Rails only uses the migration’s number (the timestamp) to identify them.Rails内部只使用migration编号(时间戳)来确定他们。Prior to Rails 2.1 the migration number started at 1 and was incremented each time a migration was generated.Rails2.1之前migration编号从1开始然后在每次migration被创建过后增加。With multiple developers it was easy for these to clash requiring you to rollback migrations and renumber them.随着开发人员的增多这样会使的很容易产生冲突这就需要你回滚migrations和重新编号他们。developers With Rails 2.1 this is largely avoided by using the creation time of the migration to identify them.Rails2.1的开发人员通过migraiton文件的创建时间指明每个文件在很大程度上避免了冲突(的发生)。You can revert to the old numbering scheme by adding the following line to config/application.rb.你可以还原带旧的版本通过在config/application.rb文件中添加如下行:

config.active_record.timestamped_migrations = false

The combination组合of timestamps and recording which migrations have been run allows Rails to handle common situations that occur with multiple developers.时间戳和migrations的名字的组合使得Rails可以处理多个开发人员的普遍情况。

For example Alice adds migrations 20080906120000 and 20080906123000 and Bob adds 20080906124500 and runs it.比如Alice 添加了migration 20080906120000 and 20080906123000以及Bob添加并运行了20080906124500Alice finishes her changes and checks in her migrations and Bob pulls down the latest changes. Alice完成了他的更改并提交在他的migrationss中,并且Bob pull down了最新的更改。Rails knows that it has not run Alices two migrations so rake db:migrate would run them (even though Bobs migration with a later timestamp has been run), and similarly migrating down would not run their down methods.

Of course this is no substitution for communication within the team.当然这些在团队交流中是不可避免的。For example, if Alices migration removed a table that Bobs migration assumed to exist, then trouble would certainly strike.例如如果Alicemigration中移除了一个表但是Bob’smigraion假设它还在,那么麻烦就来了。

1.3 Changing Migrations

Occasionally you will make a mistake when writing a migration.偶尔你在写入一个migration的时候犯个错误。If you have already run the migration then you cannot just edit the migration and run the migration again: Rails thinks it has already run the migration and so will do nothing when you run rake db:migrate.如果你已经运行了migration接着你不可能再去编辑和运行这个(错误的)migraionRails认为(你已经)运行了migration因此在你运rake db:migrate的时候不会做任何改变。You must rollback the migration (for example with rake db:rollback), edit your migration and then run rake db:migrate to run the corrected version.你必须回滚migation(例如rake db:rollback),编辑修改你的migraion然后运行正确的版本rake db:migrate

In general editing existing migrations is not a good idea: you will be creating extra work for yourself and your co-workers and cause major headaches if the existing version of the migration has already been run on production machines.在一般情况下,编辑存在的migrations不是一个好主意:因为这样你会给你自己或者你的合作成员产生额外的工作,头疼的原因是如果存在的migration已经在production机器中运行。Instead you should write a new migration that performs the changes you require.作为替代你应该编写一个新的migration来执行你需要的更改。Editing a freshly generated migration that has not yet been committed to source control (or more generally which has not been propagated beyond your development machine) is relatively harmless.编辑一个刚生成的还没有提交到软代码控制migration(或者更一般的情况还没有传播出你的开发机器)相对危害较轻。

1.4 Supported Types支持的类型

Active Record supports the following types:Active Record支持如下类型:

  • :primary_key
  • :string
  • :text
  • :integer
  • :float
  • :decimal 10进制
  • :datetime
  • :timestamp
  • :time
  • :date
  • :binary
  • :boolean

These will be mapped onto an appropriate underlying底层database type, for example with MySQL :string is mapped to VARCHAR(255).这些将会被映射为合适底层数据库的类型,例如使用MySQL :string类型将会映射成VARCHAR(255)You can create columns of types not supported by Active Record when using the non-sexy syntax, for example你可以在创建Active Record不支持的字段,使用non-sexy语法,例如

create_table :products do |t|

t.column :name, ‘polygon’, :null => false

end

This may however hinder阻碍portability移植to other databases.不过这可能会阻碍移植到其它数据库。

2 Creating a Migration新建一个Migrateion

2.1 Creating a Model新建一个Model

The model and scaffold generators will create migrations appropriate for adding a new model.modelgenerators创建器会为添加的新的model创建合适的migrationsThis migration will already contain instructions for creating the relevant有关table.这个migration已经包含在有关创建的的表的说明中。If you tell Rails what columns you want then statements声明for adding those will also be created. For example, running如果你告诉Rails将你随后声明的字段添加到创建migrations中,例如,运行

$ rails generate model Product name:string description:text

will create a migration that looks like this rails)将会新建一个像这样的migraion

class CreateProducts < ActiveRecord::Migration

def change

create_table :products do |t|

t.string :name

t.text :description

 

t.timestamps

end

end

end

You can append as many column name/type pairs as you want.你可以随意增加字段或者类型对。By default t.timestamps (which creates the updated_at and created_at columns that are automatically populated by Active Record) will be added for you.默认的t.timestampsActive Record 会自动生成updated_atcreated_at字段)会子添加在你的表中。

2.2 Creating a Standalone Migration新建一个独立的Migration

If you are creating migrations for other purposes (for example to add a column to an existing table) then you can use the migration generator:如果你正在因为其他的目的新建migrations(例如添加一个字段到一个存在的表)你可以使用migration创建器:

$ rails generate migration AddPartNumberToProducts

This will create an empty but appropriately named migration:这里会新建一个空的但是合适的migration

class AddPartNumberToProducts < ActiveRecord::Migration

def change

end

end

If the migration name is of the form “AddXXXToYYY” or “RemoveXXXFromYYY” and is followed by a list of column names and types then a migration containing the appropriate add_column and remove_column statements will be created.如果migration命名为AddXXXToYYY”RemoveXXXFromYYY”格式并且后跟有一些字段名称或类型,那么一个migration包含合适的add_columnremove_column将会被新建。

$ rails generate migration AddPartNumberToProducts part_number:string

ps:如果已经创建了一个AddPartNumberToProducts那么运行rails generate migration AddPartNumberToProducts part_number:string -f进行覆盖新建

will generate将会生成:

class AddPartNumberToProducts < ActiveRecord::Migration

def change

add_column :products, :part_number, :string

end

end

Similarly,同样,

$ rails generate migration RemovePartNumberFromProducts part_number:string

generates生成

class RemovePartNumberFromProducts < ActiveRecord::Migration

def up

remove_column :products, :part_number

end

 

def down

add_column :products, :part_number, :string

end

end

You are not limited to one magically神奇generated column, for example你不限制于一次只输入一个字段,例如

$ rails generate migration AddDetailsToProducts part_number:string price:decimal

generates生成

class AddDetailsToProducts < ActiveRecord::Migration

def change

add_column :products, :part_number, :string

add_column :products, :price, :decimal

end

end

As always, what has been generated for you is just a starting point. You can add or remove from it as you see fit.一如往常,使用migration创建器生成的migration只是一个起点,你可以添加或删除其中的字段直到你满意为止。

 

The generated migration file for destructive migrations will still be old-style using the up and down methods.在创建的migration文件中对migrations进行破坏性的操作仍然使用老式的updown方法。This is because Rails doesnt know the original data types defined when you made the original changes.这是因为当你对初始migration做了更改,Rails不知道原始的数据类型定义。

 

3 Writing a Migration

Once you have created your migration using one of the generators it’s time to get to work!一旦你已经使用一种创建器创建了你的migration,现在是时候开始工作了。

3.1 Creating a Table创建一个表

Migration method create_table will be one of your workhorses. A typical use would be Migration方法create_table将会是一个你的驮马。下面是他的典型形式

create_table :products do |t|

t.string :name

end

which creates a products table with a column called name (and as discussed below, an implicit id column).上面的代码会创建一个products表,其中包含了一个name字段(和下面讨论的一样,一个隐藏的id字段)

The object yielded to the block allows you to create columns on the table.这个migration类产生代码块让你创建表中的字段。There are two ways of doing this: The first (traditional) form looks like有两种方法创建字段:首先(传统的)方式看起来像这样

create_table :products do |t|

t.column :name, :string, :null => false

end

the second form, the so called “sexy” migration, drops the somewhat redundant column method.第二种方式,也称为sexy” migration,丢掉了有些冗余的column方法。Instead, the string, integer, etc. methods create a column of that type.作为替代,如string,interger等等方法创建相应类型的字段。Subsequent parameters are the same.其后的参赛也在同一个字段中。

create_table :products do |t|

t.string :name, :null => false

end

HABTM, hasAndBelongsToMany 这是Active Recored功能里面比较复杂的一个东西。简单的来说,就是你有一个帖子,你给这个帖子定义了多个tag但是实际上tag也是独立的,一个tag会包含很多个不同的帖子。

By default create_table will create a primary key called id.通过默认的create_table将会创建一个叫做id的主键。You can change the name of the primary key with the :primary_key option (dont forget to update the corresponding相应的model) or if you dont want a primary key at all (for example for a HABTM join table) you can pass :id => false.你可以更改主键的名字通过使用:primary_key选项(不要忘记更新其相应的model)或者你根本不想要主键(例如添加一个HABTM关系到表中)你可以通过:id => falseIf you need to pass database specific options you can place an SQL fragment in the :options option. For example如果你需要数据库的特殊选项你可以放置一个SQL片段在:options选项中。例如

create_table :products, :options => “ENGINE=BLACKHOLE” do |t|

t.string :name, :null => false

end

will append ENGINE=BLACKHOLE to the SQL statement used to create the table (when using MySQL the default is ENGINE=InnoDB).将会添加ENGINE=BLACKHOLESQL声明中用于创建表单(当使用MySQL默认的(ENGINE)是InnoDB)。

3.2 Changing Tables更改表单

A close cousin of create_table is change_table, used for changing existing tables. create_table的一个近亲是change_table,用于更改存在的表单。It is used in a similar fashion to create_table but the object yielded to the block knows more tricks.它使用的是与create_table类似的方法,但是其类产生的块能够知道更多的技巧。(如移除字段,重命名字段)For example例如

change_table :products do |t|

t.remove :description, :name

t.string :part_number

t.index :part_number

t.rename :upccode, :upc_code

end

removes the description and name columns, creates a part_number column and adds an index on it. Finally it renames the upccode column.移除description字段和name字段,创建一个part_number字段并且添加一个index(索引)给part_number。最后它重命名了:upccodeThis is the same as doing下面的代码也可以达到同样的效果

remove_column :products, :description

remove_column :products, :name

add_column :products, :part_number, :string

add_index :products, :part_number

rename_column :products, :upccode, :upc_code

 

You dont have to keep repeating the table name and it groups all the statements related to modifying one particular table.你不必重复表单名称和组相关的所有声明,(而只需要重复)修改表的一部分。The individual个别transformation转换names are also shorter, for example remove_column becomes just remove and add_index becomes just index.个别的转换方法的名称也更加简短了,例如remove_column变成removeadd_index变成index##这里是说的新旧两种方法比较

3.3 Special Helpers个别的Helpers

Active Record provides some shortcuts for common functionality.Active Record提供了一些一般功能的快捷操作。It is for example very common to add both the created_at and updated_at columns and so there is a method that does exactly that:例如很常见的通过created_atupdated_at添加字段等等,这里是一个方法的简要例子

create_table :products do |t|

t.timestamps

end

will create a new products table with those two columns (plus the id column) whereas.将会新建一个products表单,有两个字段(加上id字段)。

change_table :products do |t|

t.timestamps

end

adds those columns to an existing table.添加一些字段到存在的表中。

 

The other helper is called references (also available as belongs_to). In its simplest form it just adds some readability另一个helper被称为references(也可用belongs_to)。在其最简单的形式,它仅仅增加一些可读性。

create_table :products do |t|

t.references :category

end

will create a category_id column of the appropriate type.将会创建一个适当形式的category_id字段。Note that you pass the model name, not the column name. Active Record adds the id for you. 注意那是你关联的model名称,不是字段名称。Active Record会给你添加idIf you have polymorphic多态性belongs_to associations then references will add both of the columns required:

create_table :products do |t|

t.references :attachment, :polymorphic => {:default => ‘Photo’}

end

will add an attachment_id column and a string attachment_type column with a default value ofPhoto.将会添加一个attachment_id 和一个默认值是Photo的字符串attachment_type字段。

The references helper does not actually create foreign key constraints for you. You will need to use execute for that or a plugin that adds foreignkeysupport.references helper没有真正创建外键约束给你。你需要使用execute或者一个plugin来添加外键支持。

If the helpers provided by Active Record arent enough you can use the execute function to execute arbitrary SQL.如果Active Record提供的helper不足以满足,你可以使用execute功能来执行任意SQL

For more details and examples of individual个别methods check the API documentation, in particular the documentation for ActiveRecord::ConnectionAdapters::SchemaStatements (which provides the methods available in the up and down methods), ActiveRecord::ConnectionAdapters::TableDefinition (which provides the methods available on the object yielded by create_table) and ActiveRecord::ConnectionAdapters::Table (which provides the methods available on the object yielded by change_table).

3.4 Writing Your change Method

The change method removes the need to write both up and down methods in those cases that Rails know how to revert the changes automatically. change(migration类方法)删除需要写进updown的方法,那样Rails知道怎样自动撤销更改。Currently, the change method supports only these migration definitions:目前,change方法支持如下的migration定义:

  • add_column
  • add_index
  • add_timestamps
  • create_table
  • remove_timestamps
  • rename_column
  • rename_index
  • rename_table

If you’re going to use other methods, you’ll have to write the up and down methods normally.如果你打算使用其他方法,你可以常规的写updown方法。

3.5 Writing Your down Method

The down method of your migration should revert the transformations done by the up method.迁移的down方法应该撤销up方法做的转换。In other words the database schema架构should be unchanged if you do an up followed by a down.换句话说数据库架构应该没有改变,如果你在up方法过后又执行down方法。For example if you create a table in the up method you should drop it in the down method. It is wise to do things in precisely the reverse order to in the up method.例如如果你创建使用up方法一个表你应该在down方法中去除它。For example

class ExampleMigration < ActiveRecord::Migration

 

def up

create_table :products do |t|

t.references :category

end

#add a foreign key

execute <<-SQL

ALTER TABLE products

ADD CONSTRAINT fk_products_categories

FOREIGN KEY (category_id)

REFERENCES categories(id)

SQL

 

add_column :users, :home_page_url, :string

 

rename_column :users, :email, :email_address

end

 

def down

rename_column :users, :email_address, :email

remove_column :users, :home_page_url

execute “ALTER TABLE products DROP FOREIGN KEY fk_products_categories”

drop_table :products

end

end

Sometimes your migration will do something which is just plain irreversible不可逆转的, for example it might destroy some data.有时候你的migration将会做一些不可逆转的事情。例如它可能会删除一些数据。In cases like those when you can’t reverse the migration you can raise ActiveRecord::IrreversibleMigration from your down method.在这样的情况中你不能撤销migration你可以在你的down方法中raise ActiveRecord::IrreversibleMigrationIf someone tries to revert your migration an error message will be displayed saying that it can’t be done.如果有人尝试撤销你的migration那么一个错误消息将会被显示it can’t be done”

4 Running Migrations

Rails provides a set of rake tasks to work with migrations which boils down to running certain sets of migrations. Rails提供给migrations一组rake任务,它大致归纳于运行若干的migrationsThe very first migration related rake task you use will probably be db:migrate.你使用的非常靠前的migration相应于rake的任务恰好是db:migrateIn its most basic form it just runs the up method for all the migrations that have not yet been run.其中它是最基本的形式,它仅仅运行migrations 还没被运行的up方法。If there are no such migrations it exits.如果没有这样的迁移它将会退出。

Note that running the db:migrate also invokes the db:schema:dump task, which will update your db/schema.rb file to match the structure of your database.注意运行db:migrate也调用了db:schema:dump任务,它将会更新你的db/schema.rb来和你的数据库匹配。

If you specify a target version, Active Record will run the required migrations (up or down) until it has reached the specified version.如果你特别(指定了)一个目标版本,Active Record将会运行所请求的migrationsupdown)直到它已经达成了(与)指定版本(的匹配)。The version is the numerical prefix on the migrations filename.版本号是migrations文件的数字前缀。For example to migrate to version 20080906120000 run例如迁移到20080906120000版本

$ rake db:migrate VERSION=20080906120000

If this is greater than the current version (i.e. it is migrating upwards) this will run the up method on all migrations up to and including 20080906120000, if migrating downwards this will run the down method on all the migrations down to, but not including, 20080906120000.如果执行的版本比当前版本更优(新)(i.e. it is migrating upwards)这将会执行包含20080906120000的所有版本的migrations up方法,反之则执行不包括20080906120000的版本之外的所有版本的migrations down方法。

4.1 Rolling Back

A common task is to rollback the last migration, for example if you made a mistake in it and wish to correct it.一个常用的任务来回滚最新的migration,例如如果你在其中犯了一个错误希望Rails能够改正它。Rather than tracking down the version number associated with the previous migration you can run

$ rake db:rollback

This will run the down method from the latest migration.这将会对最新的migration执行down方法。If you need to undo several migrations you can provide a STEP parameter:如果你需要撤销一系列的migrations你可以提供一个STEP 参数:

$ rake db:rollback STEP=3

will run the down method from the last 3 migrations.将会对最近的3次迁移执行down方法。

The rake db:migrate:redo task is a shortcut for doing a rollback and then migrating back up again. rake db:migrate:redo任务是一个回滚后再次返回的快捷操作。As with the db:rollback task you can use the STEP parameter if you need to go more than one version back, for example通过dbrollback 任务你可以使用STEP参数如果你需要撤销超过一个版本,例如

$ rake db:migrate:redo STEP=3

Neither of these Rake tasks do anything you could not do with db:migrate, they are simply more convenient方便since you do not need to explicitly specify the version to migrate to.(通过)上两次rake任务,你没有对db:migrate任何事情,如果你不需要指定明确的版本来迁移这样做都非常方便。

Lastly, the db:reset task will drop the database, recreate it and load the current schema into it.最后,

db:reset任务将会drop数据库,重建它并且在其中导入正确的架构。

This is not the same as running all the migrationssee the section on schema.rb.这和运行所有的migrations不一样——阅读schema.rb章节。

4.2 Being Specific开始指定的

If you need to run a specific migration up or down the db:migrate:up and db:migrate:down tasks will do that. 如果你需要对一个指定的版本migration执行updown,那么db:migrate:up and db:migrate:down任务会满足你。Just specify the appropriate version and the corresponding migration will have its up or down method invoked, for example指定适当的版本那么相应的migration就会调用它的updown方法,例如

$ rake db:migrate:up VERSION=20080906120000

will run the up method from the 20080906120000 migration.将会执行来自20080906120000版本的migrationup方法。These tasks check whether the migration has already run, so for example db:migrate:up VERSION=20080906120000 will do nothing if Active Record believes that 20080906120000 has already been run.这个任务会检查migration是否已经运行,如果Active Record认为已经被运行过了db:migrate:up VERSION=20080906120000将不会做任何事情。

4.3 Being Talkative开始唠叨

By default migrations tell you exactly what they’re doing and how long it took.通过默认的migrations会明确地告诉你他们做了什么以及花费了多长时间。A migration creating a table and adding an index might produce output like this一次迁移创建一个表并且添加一个index可能会产生这样的输出

20080906170109 CreateProducts: migrating

create_table(:products)

–> 0.0021s

add_index(:products, :name)

–> 0.0026s

20080906170109 CreateProducts: migrated (0.0059s)

Several methods are provided that allow you to control all this:一些方法(被证明)允许你像下面这样控制

  • suppress_messages takes a block as an argument and suppresses抑制any output generated by the block. suppress_messages获取一个block作为一个参数并且抑制常规输出到block
  • say takes a message argument and outputs it as is. A second boolean argument can be passed to specify whether to indent缩进or not. say获取一个message参数并且输出它。第二个布尔参数可以指定是否缩进
  • say_with_time outputs text along with how long it took to run its block. If the block returns an integer it assumes it is the number of rows affected. say_with_time在文字旁边输出运行这个代码块花费了多少时间。如果block返回一个整数它假设这是受影响的代码行。

For example, this migration例如,这个migration

class CreateProducts < ActiveRecord::Migration

def change

suppress_messages do

create_table :products do |t|

t.string :name

t.text :description

t.timestamps

end

end

say “Created a table”

suppress_messages {add_index :products, :name}

say “and an index!”, true

say_with_time ‘Waiting for a while’ do

sleep 10

250

end

end

end

generates the following output一般情况会得到如下输出

== CreateProducts: migrating =================================================

— Created a table

–> and an index!

— Waiting for a while

–> 10.0108s

–> 250 rows

== CreateProducts: migrated (10.0171s) =======================================

If you just want Active Record to shut up then running rake db:migrate VERBOSE=false will suppress抑制all output.如果你希望让所有Active Record闭嘴,那么运行rake db:migrate VERBOSE=false会抑制所有的输出。

5 Using Models in Your Migrations在你的migration中使用Moldels

When creating or updating data in a migration it is often tempting to use one of your models新建或更新一个migration的数据它通常会使你的models更有诱惑力。After all they exist to provide easy access to the underlying底层data.毕竟他们的存在提供了对底层数据的访问。This can be done, but some caution注意should be observed观察.这是可行的,但是有些地方需要注意。

For example, problems occur when the model uses database columns which are (1) not currently in the database and (2) will be created by this or a subsequent migration.例如,问题焦点当models使用数据库字段,但是migration1)在数据库中没有这个字段而migration2)将会被它本身的migration文件或者随后的migration创建。

Consider this example, where Alice and Bob are working on the same code base which contains a Product model:思考这个例子,AliceBob工作在同样的代码中,其主要包含一个Product模型:

Bob goes on vacation. Bob在休假

Alice creates a migration for the products table which adds a new column and initializes it. She also adds a validation to the Product model for the new column. Alice创建一个products表单的migration,其添加一个新的字段并初始化它。她也添加一个关于新字段的验证到Product模型。

rails generate migration AddFlagToProduct flag:int#然后再添加一部分这样通过创建器的话model文件也一起创建了

# db/migrate/20100513121110_add_flag_to_product.rb

 

class AddFlagToProduct < ActiveRecord::Migration

def change

add_column :products, :flag, :int

Product.all.each { |f| f.update_attributes!(:flag => ‘false’) }#初始化置为false

end

end

 

# app/model/product.rb

 

class Product < ActiveRecord::Base

validates :flag, :presence => true

end

Alice adds a second migration which adds and initializes another column to the products table and also adds a validation to the Product model for the new column.Alice添加第二个migration,在这个migration中给Products表单添加并初始化了另一个字段,而且为Product model的新字段添加并初始化了验证。

# db/migrate/20100515121110_add_fuzz_to_product.rb

 

class AddFuzzToProduct < ActiveRecord::Migration

def change

add_column :products, :fuzz, :string

Product.all.each { |f| f.update_attributes! :fuzz => ‘fuzzy’ }

end

end

# app/model/product.rb

 

class Product < ActiveRecord::Base

validates :flag, :fuzz, :presence => true

end

Both migrations work for Alice.两个migrations都是Alice编写的。

Bob comes back from vacation假期and:Bob休假回来然后接着下面的工作

  1. updates the sourcewhich contains both migrations and the latests version of the Product model. 更新源代码——包含migrations和最新版本的Product模型。
  2. runs outstanding migrations with rake db:migrate, which includes the one that updates the Product model. 通过rake db:migrate执行突出(最新版本的migration)的migrations,其中包含了更新Product模型。

The migration crashes because when the model attempts尝试to save, it tries to validate the second added column, which is not in the database when the first migration runs.数据迁移冲突因为当model尝试保存时,它试图验证第二个添加的字段,然而它在第一次migration运行时不在数据库中。(没有弄明白呢感觉好像两个迁移是分开的才会出现这样的错误)

rake aborted!

An error has occurred, this and all later migrations canceled:

 

undefined method `fuzz' for #<Product:0x000001049b14a0>

A fix for this is to create a local model within the migration.解决这个错误是在migration创建一个本地的modelThis keeps rails from running the validations, so that the migrations run to completion.这样在rails运行中保持了验证,使得migrations完成了验证。

When using a faux model, it’s a good idea to call Product.reset_column_information to refresh the ActiveRecord cache for the Product model priorto updating data in the database.当使用一个人造的model,调用Product.reset_column_information来刷新在更新数据到数据库前的Product modelActiveRecord缓存是一个好主意。

If Alice had done this instead, there would have been no problem:如果Alice这样做了就不会有问题了

# db/migrate/20100513121110_add_flag_to_product.rb

 

class AddFlagToProduct < ActiveRecord::Migration

class Product < ActiveRecord::Base

end

 

def change

add_column :products, :flag, :int

Product.reset_column_information

Product.all.each { |f| f.update_attributes!(:flag => false) }

end

end

 

class AddFuzzToProduct < ActiveRecord::Migration

class Product < ActiveRecord::Base

end

def change

add_column :products, :fuzz, :string

Product.reset_column_information

Product.all.each { |f| f.update_attributes! :fuzz => ‘fuzzy’ }

end

end

 

6 Schema Dumping and You架构模式和你

6.1 What are Schema Files for?

Migrations, mighty as they may be, are not the authoritative source for your database schema. Migrations,很可能不是你的数据库schema的授权源。That role falls to either db/schema.rb or an SQL file which Active Record generates by examining the database.这一规则影响到db/schema.rbSQL文件它是Active Record创建器通过检查数据库生成的。They are not designed to be edited, they just represent the current state of the database.他们的设计(原意)是不被编辑的,他们仅仅是当前数据库状态的一个表现。

There is no need (and it is error prone) to deploy部署a new instance of an app by replaying the entire整个migration history. It is much simpler and faster to just load into the database a description of the current schema.这里没有必要(并且是错误倾向)去通过replaying整个migration历史来部署一个app的新的实例。更明智和更快捷的方式是load数据库的描述(也就是)当前schema

For example, this is how the test database is created: the current development database is dumped (either to db/schema.rb or db/development.sql) and then loaded into the test database.例如,这是测试数据库怎样被创建的:当前的开发数据库已经被转储(要么db/schema.rb 要么db/development.sql)然后被导入test数据库。

Schema files are also useful if you want a quick look at what attributes an Active Record object has. Schema文件在你希望快速的查看Active Record object有些什么属性的时候也很有帮助。This information is not in the model’s code and is frequently spread across several migrations but is all summed up in the schema file. 这些信息不是model中的代码并且频繁分布在多个migrations但是是所有架构文件的总结。The annotate_models plugin, which automatically adds (and updates) comments at the top of each model summarizing the schema, may also be of interest. annotate_models 插件,它能够自动在每个model汇总的顶部的添加(更新)comments,可能你会有兴趣。

6.2 Types of Schema Dumps架构转储的形式

There are two ways to dump the schema. This is set in config/application.rb by the config.active_record.schema_format setting, which may be either :sql or :ruby.有两种方式来转储架构。这是(相关)设置在config/application.rb通过config.active_record.schema_format来设置,其形式要么是:sql要么是:ruby.

If :ruby is selected then the schema is stored in db/schema.rb. If you look at this file youll find that it looks an awful可怕lot like one very big migration:如果:ruby被选中那么schema被存储在db/schema.rb。如果你查看这个文件你将会发现它看起来就像一个很大的migration

ActiveRecord::Schema.define(:version => 20080906171750) do

create_table “authors”, :force => true do |t|

t.string “name”

t.datetime “created_at”

t.datetime “updated_at”

end

 

create_table “products”, :force => true do |t|

t.string “name”

t.text “description”

t.datetime “created_at”

t.datetime “updated_at”

t.string “part_number”

end

end

In many ways this is exactly what it is.在很多情况下它能够准确的(反应出schema)的信息。This file is created by inspecting the database and expressing its structure using create_table, add_index, and so on.这个文件被创建来检查数据库以及表达它的结构使用create_table, add_index等等。Because this is database independent it could be loaded into any database that Active Record supports.由于数据库的独立性它可以被导入到Active Record支持的任何数据库中。This could be very useful if you were to distribute an application that is able to run against multiple databases.这会非常有用如果你发行一个应用程序它可能面对多个数据库运行。

There is however a trade-off: db/schema.rb cannot express database specific items such as foreign key constraints, triggers or stored procedures.然而,有一个权衡:DB/ schema.rb不能表达数据库的具体项目,如外键约束,触发器或存储过程。While in a migration you can execute custom SQL statements, the schema dumper cannot reconstitute those statements from the database.虽然在一个migration你可以执行自定义的SQL表达式,schema储存器不能修复一些SQL的表达式(不能实现migrationrollback等功能)。If you are using features like this then you should set the schema format to :sql.如果你正在使用这样的特性接着你应该设置schema 格式为:sql

Instead of using Active Records schema dumper the databases structure will be dumped using a tool specific to that database (via the db:structure:dump Rake task) into db/#{Rails.env}_structure.sql. For example for PostgreSQL the pg_dump utility is used and for MySQL this file will contain the output of SHOW CREATE TABLE for the various tables. Loading this schema is simply a question of executing the SQL statements contained inside.

By definition this will be a perfect copy of the database’s structure but this will usually prevent loading the schema into a database other than the one used to create it.根据定义,这将是一个完美的复制数据库的结构,但通常会阻止加载到比其他用于创建一个数据库的架构。

6.3 Schema Dumps and Source Control

Because schema dumps are the authoritative source for your database schema, it is strongly recommended建议that you check them into source control.因为架构转储是你的数据库架构的授权源,强烈建议你检查他们到源控制。

7 Active Record and Referential参照Integrity完整

The Active Record way claims that intelligence belongs in your models, not in the database. As such, features such as triggers or foreign key constraints, which push some of that intelligence back into the database, are not heavily used.

Validations such as validates :foreign_key, :uniqueness => true are one way in which models can enforce data integrity. The :dependent option on associations allows models to automatically destroy child objects when the parent is destroyed. Like anything which operates at the application level these cannot guarantee referential integrity and so some people augment them with foreign key constraints.

Although Active Record does not provide any tools for working directly with such features, the execute method can be used to execute arbitrary SQL. There are also a number of plugins such as foreign_key_migrations which add foreign key support to Active Record (including support for dumping foreign keys in db/schema.rb).

 

HTML4和HTML5之间的10个主要不同

HTML4和HTML5之间的10个主要不同

HTML5是最新的HTML标准,或迟或早,所有的web程序员都会发现需要使用到这个最新的标准,而且,很多人都会感觉到,重新开发一个HTML5的网站,要比把一个网站从HTML4迁移到HTML5上容易的多,这是因为这两个版本之间有很大不同之处。

事实上,HTML5并没有对HTML4做什么重大的修改,它们很多东西都是相似的。

可是,其中有一些很重要的区别你需要知道。下面列出的就是一些HTML4和HTML5之间主要的不同之处(并不是全部,全部列出来是不可能的):

  1. HTML5标准还在制定中 这头一个不同之处显而易见,但非常重要,我需要先从它开始。也许你已经注意到了关于HTML5很酷的言论到处都是,但是事实情况是,HTML5是一个还未完成的标准。HTML4已经有10岁了,但它仍是当前正式的标准的事实没有改变。

另一方面,HTML5仍处在早期阶段,以后的修改会不断的出现。你必须考虑到这些,因为你在网站上使用的这些新增加或修改的网页元素会每年都出现一些变化,你需要不停的更新升级你的网站,这可不是你希望的。这就是目前为止,你最好在产品里使用HTML4,只在实验里使用HTML5的原因。

  1. 简化的语法 更简单的doctype声明是HTML5里众多新特征之一。现在你只需要写<!doctype html>,这就行了。HTML5的语法兼容HTML4和XHTML1,但不兼容SGML。

  2. 一个替代Flash的新 <canvas> 标记 对于Web用户来说,Flash既是一个惊喜,也是一种痛苦。有很多的Web开发人员对HTML5对Flash产生的威胁很不满。但对于那些忍受着要花几年时间加载和运行的臃肿的Flash视频的人来说,用新的 <canvas> 标记生成视频的技术已经到来。

目前, <canvas> 标记并不能提供所有的Flash具有的功能,但假以时日,Flash必将从web上淘汰。我们拭目以待,因为很多人还并不认同这种观点。

  1. 新的 <header> 和 <footer> 标记 HTML5的设计是要更好的描绘网站的解剖结构。这就是为什么这些<header> 和<footer> 等新标记的出现,它们是专门为标志网站的这些部分设计的。

在开发网站时,你不在需要用<div>标记来标注网页的这些部分。

  1. 新的 <section> 和 <article> 标记 跟<header> 和 <footer>标记类似,HTML5中引入的新的<section> 和 <article> 标记可以让开发人员更好的标注页面上的这些区域。

据推测,除了让代码更有组织外,它也能改善SEO效果,能让搜索引擎更容易的分析你的页面。

  1. 新的 <menu> 和 <figure> 标记 新的<menu>标记可以被用作普通的菜单,也可以用在工具条和右键菜单上,虽然这些东西在页面上并不常用。

类似的,新的 <figure> 标记是一种更专业的管理页面上文字和图像的方式。当然,你可以用样式表来控制文字和图像,但使用HTML5内置的这个标记更适合。

  1. 新的 <audio> 和 <video> 标记 新的<audio> 和 <video> 标记可能是HTML5中增加的最有用处的两个东西了。正如标记名称,它们是用来嵌入音频和视频文件的。

除此之外还有一些新的多媒体的标记和属性,例如<track>,它是用来提供跟踪视频的文字信息的。有了这些标记,HTML5使Web2.0特征变得越来越友好。问题在于,在HTML5还未被广泛的接受之前,Web2.0还是老的Web2.0。

  1. 全新的表单设计 新的 <form> 和 <forminput> 标记对原有的表单元素进行的全新的修改,它们有很多的新属性(以及一些修改)。如果你经常的开发表单,你应该花时间更详细的研究一下。

  2. 不再使用 <b> 和 <font> 标记 对我个人来说,这是一个让我不太理解的改动。我并不认为去除 <b> 和 <font>标记会带来多大的好处。我知道,官方的指导说这些标记可以通过CCS来做更好的处理,但这样一来,为了在文章一两个地方出现的这种标记,你就需要在独立的css和文本两个地方来实现这一的功能,岂不笨拙。也许我们以后会习惯这种方法。

  3. 不再使用 <frame>, <center>, <big> 标记 事实上,我已经记不清曾经何时用过这些标记了,所以,我并不为去除这些标记感到悲哀。相同的原因,有更好的标记能实现它们的功能——这很好,任何作废的标记从标准中剔除都是受欢迎的。

这10个HTML5和HTML4之间的不同只是整个新的规范中的一小部分。除了这些主要的变动外,我还可以略提一下一些次要的改动,比如修改了<ol> 标记的属性,让它能够倒排序,对<u>标记也做了修改。

所有这些次要的改动数量众多。而且新的修改也在不断的增加,因此,如果你想实时跟踪最新的动向,你需要经常的查看w3.org的HTML4 和 HTML5之间的不同这个页面。如果你很心急,想在你的工作中使用这些新的标记和属性,我劝告你最好只是做实验,原因已经说的很清楚了,这些新标记和新属性在将来也许会有很大的改变,所以,除非你不断的更新你的代码,它们很可能会过期失效。

尽管如今大多数流行的浏览器的最新版都支持HTML5,但有些新的(或修改的)标记和属性它们并不支持,所以你的网页在用户的屏幕上有可能前后显示的不一致。耐心等待,等待HTML5真正可以实用时候。目前还不是时候。

日历js代码

日历js代码

a simple js calendar code:

code1:








3 4

Code2:








3 4

苹果饥饿营销自造尴尬 “黄牛”堵门叫卖iPhone4s

苹果饥饿营销自造尴尬 “黄牛”堵门叫卖iPhone4s

iPhone4s已经在几十个国家和地区上市销售,并于本月11日正式登陆中国香港,但因为iPhone4s在内地上市时间迟迟未定,已经望穿秋水的“果粉”只能继续守候下去。然而想继续通过饥饿营销积累客户的苹果,不得不面对自己制造的“尴尬”。随着昨日大量港版iPhone4s抵京,大批黄牛来到苹果三里屯官方零售店外堵门叫卖。

  “iPhone4s要吗?港版无锁的!”在苹果三里屯零售店门口,一群手拿着苹果白色包装盒的黄牛,不断地招揽着过往行人,一发现有顾客从苹果店走出来,就立刻围上去。

  一位刘姓男子透露,他卖的iPhone4s是昨天刚从深圳发到北京的,黑色16G版的现在卖5850元,不讲价。虽然比起4150元的官方定价,这名黄牛一口气加了1700元,但他表示这个价格还是港版上市后降了的,以前澳版的iPhone4s要价都超过6500元。这名黄牛“备货”十分充足,身后的书包里装了近百台iPhone4s。

  隔着一道通明的玻璃墙,苹果店员自然对门外的黄牛一清二楚,除了在店门口立起一块不大的警示牌提醒消费者黄牛手中的iPhone4s“来源不明”外,苹果对黄牛们似乎无能为力。“iPhone4s的上市时间还没定,所以现在我们还没有相应的预订服务。”三里屯苹果旗舰店的一位负责人坦言,想从苹果官方渠道买到iPhone4s,可能还要等上很长一段时间。

  面对苹果没货,黄牛堵门的情况,不禁有“果粉”发出疑问,“为什么iPhone4s在国内迟迟不能上市”。对此,苹果中国的负责人解释称,iPhone4s进入国内需要经过多重审核,以取得工信部的入网许可证,所以上市时间迟迟未定。然而苹果这一解释无法服众,一位业内人士表示,工信部的入网许可并不难拿,一般企业提出申请后10个工作日就能通过相应审核,而iPhone4s从发布至今已经1个多月。

  “苹果之所以迟迟不定iPhone4s在中国内地的上市时间,无非还是想玩饥饿营销,吊起消费者的胃口。”这位业内人士直言,但如此营销造成黄牛堵门,不知苹果有何感想。

码农熬夜指南

码农熬夜指南

最近赶着交作业,所以不得已连着三天熬夜,感觉整个人都快斯巴达了。到处找了些关于熬夜的技巧,与大家共勉。当然,不用熬夜是最好啦,可惜作为天生乐观派的程序员,多数都是平时不用功,到了要交付的时候才伴随着最后期限呼啸而过的声音熬夜赶进度。 首先,能不熬夜还是不熬的好。 熬夜前的准备 下午时候,不要吃泡面来填饱肚子,以免火气太大。晚餐应多补充一些含维生素C或含有胶原蛋白的食物,鱼类、豆类这些。 拿热水泡脚很养人,对于熬夜时候保持精力也很管用。 开始熬夜前,来一颗维他命B群营养丸,维他命B能够解除疲劳,增强人体免疫力。拿医保卡去学校随便拿一瓶,也就是几十块的事情。 熬夜中 熬夜的人,最先想到的就是喝咖啡提神。但是咖啡因虽然提神,相对地会消耗体内与神经、肌肉协调有关的维他命B群,缺乏维他命B群的人本来就比较容易累,更可能形成恶性循环,养成酗茶、酗咖啡的习惯,需要量愈来愈多,效果却愈来愈差。因此,我个人比较推荐喝茶,绿茶很不错,可以提神,又可以消除体内多余的自由基,据说还有些防辐射的作用。不过貌似肠胃不好的人喝不得绿茶,那枸杞大枣茶或菊花茶也是很好的选择,提神而且有去火的功效。 熬夜时候不要吃太多甜食,高糖虽有高热量,刚开始让人兴奋,却会消耗维他命B群,会让人容易疲劳,也容易引来肥胖问题。 熬夜时候最好的食物是水果,这个道理就不用多说了。值得注意的是,花生米、杏仁、腰果、胡桃等干果类食品,含有丰富的蛋白质、维生素B、维生素E、钙和铁等矿物质以及植物油,而胆固醇的含量很低,对恢复体能有特殊的功效。绝对是熬夜必备之选啊。 熬夜时,大脑需氧量会增大,最好隔一个小时到走廊做一些简单易行的肌肉放松动作,一方面舒缓筋骨,预防颈椎病之类的;另一方面,可以多呼吸一些新鲜空气,提神又减压。 熬夜后 其实,熬夜时候因为要忙着码代码,精神一直紧绷着,倒是并不难受。反倒是熬夜之后,才是真正痛苦的时候,精神萎靡、头疼欲裂都是常事儿。这个确实没什么好办法,只能说按之前技巧来做,痛苦会少一些。  熬完夜直接补觉到中午,我个人觉得这是最好的恢复方式。因为千理由万理由,熬夜最让人难受的还是睡眠时间不够。 如果没这个条件,那中午也至少要睡一个小时。常言道,“午睡一小时抵过晚上睡三小时”。 大体就这些了,大家有些好的建议也不妨提出来。要想熬夜精神好,就得有技巧啊。

Getting Started With Rails-(EN-CN)

Getting Started with Rails-(EN-CN)

Getting Started with Rails

This guide covers getting up and running with Ruby on Rails. After reading it, you should be familiar with:本指导手册覆盖了入门和运行ruby on rails,通过阅读本指导,你会了解到:

  • Installing Rails, creating a new Rails application, and connecting your application to a database
  • The general layout#布局of a Rails application 应用程序的一般布局
  • The basic principles of MVC (Model, View Controller) and RESTful design MVC的基本原则和RESTful设计(理念)
  • How to quickly generate the starting pieces of a Rails application 如何快捷的开始一个Rails应用

ThisGuideisbasedonRails3.1.SomeofthecodeshownherewillnotworkinearlierversionsofRails.这个指导手册适用与Rails3.1,有些代码在Rails早期版本可能会不工作。

1 Guide Assumptions假设

1 Guide Assumptions

This guide is designed for beginners who want to get started with a Rails application from scratch#刻痕. It does not assume#假设that you have any prior#预先experience with Rails. However, to get the most out of it, you need to have some prerequisites installed:本指导设计给那些想大概的了解Rails应用创建的初学者。这里假设你对Rails没有任何预先的了解。不管怎样要得到(本手册的)知识,你需要预先安装:

  • The Ruby language version 1.8.7 or higher

Note that Ruby 1.8.7 p248 and p249 have marshaling#封送处理bugs that crash#意外崩溃紧急Rails 3.0. Ruby Enterprise Edition have these fixed since release 1.8.7-2010.02 though. Ruby 1.8.7 p248 and p249发行版1.8.7-2010.02 已经得到解决。On the 1.9 front, Ruby 1.9.1 is not usable because it outright segfaults on Rails 3.0, so if you want to use Rails 3 with 1.9.x jump on 1.9.2 for smooth sailing.

Rails is a web application framework running on the Ruby programming language. If you have no prior experience with Ruby, you will find a very steep#learning curve diving straight into Rails. There are some good free resources on the internet for learning Ruby, including: Rails是一个基于Ruby程序语言的web程序框架。如果你没有预先的学习ruby,你会发现直接的入门Rails学习很有跨度。这里有一些学习ruby的免费互联网资源。

2 What is Rails?什么Rails

Rails is a web application development framework written in the Ruby language. It is designed to make programming web applications easier by making assumptions#完成about what every developer needs to get started. It allows you to write less code while accomplishing more than many other languages and frameworks. Experienced#经验丰富的Rails developers also report that it makes web application development more fun.Rails是一个使用Ruby语言编写的的web框架应用程序。其设计目的是为了让每个着手开始编写web应用程序的开发人员更加容易的完成工作。它允许你写最少的代码完成超过其他任何语言和框架所完成的工作。经验丰富的Rails开发人员还告诉我们通过Rails使设计web应用程序更快乐。

Rails is opinionated#自以为是software. It makes the assumption that there is a “best” way to do things, and it’s designed to encourage#鼓励that way – and in some cases to discourage alternatives替代品. If you learn “The Rails Way” you’ll probably discover a tremendous increase in productivity. If you persist in bringing old habits#习惯from other languages to your Rails development, and trying to use patterns模式you learned elsewhere, you may have a less happy experience.Rails是一个自以为是的软件。它使得我们以最好的方式去做事情,它还鼓励——and in some cases to discourage alternatives,如果你学习Rails方式你将会适时的发现生产力的巨大增长。如果你固守在来自其他语言的旧的习惯去进行你的Rails开发,以以它地方学来的模式尝试Rails,那么你将会得到很少的快乐的经历。

The Rails philosophy理念includes several guiding principles:Rails理念包含几个指导原则

  • DRY – “Dont Repeat Yourself – suggests that writing the same code over and over again is a bad thing. 不要自己重复——建议一次又一次编写同样的代码是一件坏事请。
  • Convention#约定Over Configuration(约定优于配置) – means that Rails makes assumptions about what you want to do and how you’re going to do it想做怎么做, rather than requiring you to specify every little thing through endless configuration files. 约定优于配置——意思是Rails对于你想做什么以及你想怎么做,你刻意的做很少的事情比编写无尽的配置文件更好
  • REST is the best pattern模式for web applications – organizing your application around resources#资源and standard HTTP verbs#动词is the fastest way to go. REST是开发web应用程序的最好模式——环绕resources和标准的HTTP动作组织你的应用程序

2.1 The MVC Architecture#MVC架构

At the core of Rails is the Model, View, Controller architecture, usually just called MVC. MVC benefits include:ModelViewController架构是Rails的核心,通常称之为MVC

  • Isolation分离of business logic逻辑from the user interface 界面#从用户界面的业务逻辑的分离
  • Ease of keeping code DRY# Dont Repeat Yourself
  • Making it clear where different types of code belong for easier maintenance #维护明确代码的不同之处使之跟容易维护
2.1.1 Models

A model represents代表the information (data) of the application and the rules to manipulate that data.模型代表了应用程序的信息(数据)和操纵这些数据的规则。In the case of Rails, models are primarily used for managing the rules of interaction with a corresponding#相应的database table. In most cases, each table in your database will correspond to one model in your application. The bulk of your application’s business logic will be concentrated in the models.Rails中,models主要用于管理数据表和相应的规则的互动。在大多数情况,在你数据库中的每个表都会和你的应用程序互动。你应用程序的逻辑业务将会集中放置在models中。

2.1.2 Views

Views represent the user interface of your application. In Rails, views are often HTML files with embedded Ruby code that perform执行tasks related solely to the presentation#演示of the data. Views handle the job of providing data to the web browser or other tool that is used to make requests from your application.View代表了应用程序的用户界面。在Rails中,Views通常是嵌入了执行演示数据任务的Ruby代码的HTML文件。Views完成了给web浏览器或者其他工具用于提出来自你的程序的请求提供数据。

2.1.3 Controllers

Controllers provide the “glue” between models and views. In Rails, controllers are responsible for processing the incoming requests from the web browser, interrogating the models for data, and passing that data on to the views for presentation.Controllers提供了models和视图间的粘合。在Rails中,controllers相应来自web浏览器请求的进程,向models询问数据以及传递数据给views用于演示。

2.2 The Components of Rails Rials的组件

Rails ships as many individual components.Rails关联着许多独立的组件。Each of these components are briefly explained below. 对这些组建在下面给出简要的解释。If you are new to Rails, as you read this section, don’t get hung up on the details of each component, as they will be explained in further detail later. 如果你是Rails的新人,当你阅读到这个部分,不要抛弃任何一个组件的描述,而且他们还会做进一步解释。For instance, we will bring up Rack applications, but you don’t need to know anything about them to continue with this guide.例如,我们将会构造应用骨架,但是在这里,你不需要知道关于他们的更进一步的任何知识。

  • Action Pack动作行为组
    • Action Controller 行为控制
    • Action Dispatch 行为传输
    • Action View 行为视图
  • Action Mailer 行为信封
  • Active Model
  • Active Record
  • Active Resource
  • Active Support
  • Railties
2.2.1 Action Pack

Action Pack is a single gem that contains Action Controller, Action View and Action Dispatch. The “VC” part of “MVC”.Action Pack是一个单独的包它包含了Action Controller, Action View and Action Dispatch传输调度。是MVCVC部分

2.2.1.1 Action Controller

Action Controller is the component that manages the controllers in a Rails application.The Action Controller framework processes incoming requests to a Rails application, extracts parameters, and dispatches them to the intended action. Services provided by Action Controller include session management, template rendering, and redirect management. Action Controller是在Rails中管理控制的组件。The Action Controller框架进程收到Rails应用程序的请求,提取参数,以及调度他们到具有相应义务的动作。这些服务是由Action Controller包含了会话管理部分,模板渲染(翻译),重定向部分。

2.2.1.2 Action View

Action View manages the views of your Rails application. It can create both HTML and XML output by default. Action View manages rendering templates, including nested and partial templates, and includes built-in AJAX support. View templates are covered in more detail in another guide called LayoutsandRendering.Action View管理你的Rails应用程序的视图。它可以以创建HTMLXML作为默认输出。Action View管理模板渲染,包含嵌套和组装模板,还包含了内置的AJAX支持。

2.2.1.3 Action Dispatch

Action Dispatch handles routing of web requests and dispatches them as you want, either to your application or any other Rack application. Rack applications are a more advanced topic and are covered in a separate guide called RailsonRack.行为调度处理了你的和其他部分的应用程序的路由——这些web请求以及一些你所想要的调度,

2.2.2 Action Mailer

Action Mailer is a framework for building e-mail services. You can use Action Mailer to receive and process incoming email and send simple plain text or complex multipart emails based on flexible templates.Action Mailer是一个营造E-mail服务的框架。你可以使用Action Mailer去发送、接收emial以及发送一些简单的计划文本或者基于灵活模板的复杂的多重的电子邮件。

2.2.3 Active Model

Active Model provides a defined interface between the Action Pack gem services and Object Relationship Mapping gems such as Active Record. Active Model allows Rails to utilize other ORM frameworks in place of Active Record if your application needs this.Active Model提供了Action Pack gem服务和Object Relationship Mapping gems之间的接口定义,比如Active记录。Active Model允许RailsActive Record部分去采用其他ORM框架如果你的应用程序需要。

2.2.4 Active Record

Active Record is the base for the models in a Rails application. It provides database independence, basic CRUD functionality, advanced finding capabilities, and the ability to relate models to one another, among other services.Active Record是一个Rails应用程序的models根本。它提供独立的数据库,基于CRUD功能,先进(高级)的查找能力,和与另一个models关联的能力,几乎所有其他服务。

2.2.5 Active Resource

Active Resource provides a framework for managing the connection between business objects and RESTful web services. It implements a way to map web-based resources to local objects with CRUD semantics.Active Resource提供一个管理目标业务和RESTful web服务之间连接的框架。它实现了使用CRUD语义测绘web-base 资源到本地目标。

2.2.6 Active Support

Active Support is an extensive collection of utility classes and standard Ruby library extensions that are used in Rails, both by the core code and by your applications.Active Support是一个广泛收集实用工具类和标准的Ruby库的扩展,它们由的核心代码和您的应用程序(决定)。

2.2.7 Railties

Railties is the core Rails code that builds new Rails applications and glues the various frameworks and plugins together in any Rails application.Railties是在Rails代码中创建新Rails应用和在任何Rails应用中粘和各种插件在一起的核心。

2.3 REST

Rest stands for Representational State Transfer and is the foundation of the RESTful architecture.Rest作为具有代表性的状态传输是RESTful架构的基础。This is generally considered to be Roy Fielding’s doctoral thesis博士论文, ArchitecturalStylesandtheDesignofNetwork-basedSoftwareArchitectures. While you can read through the thesis, REST in terms of Rails boils down to two main principles:当你阅读这篇论文RESTRails归纳为主要亮点原则:

  • Using resource identifiers such as URLs to represent resources.使用资源标识符比如URLs去表现资源
  • Transferring representations of the state of that resource between system components.转移(传送)在系统组件之间代表资源的状态

For example, the following HTTP request:

DELETE /photos/17

would be understood to refer to a photo resource with the ID of 17, and to indicate a desired action – deleting that resource. (系统)将会明白参照ID14phone资源,注明删除该资源。REST is a natural style for the architecture of web applications, and Rails hooks into this shielding屏蔽you from many of the RESTful complexities and browser quirks.REST自然风格去架构web应用程序,通过组件这样的钩子,使你避免了许多复杂的RESTful和浏览器之间的差异。

If you’d like more details on REST as an architectural#架构风格style, these resources are more approachable平易近人than Fielding’s thesis:

3 Creating a New Rails Project

If you follow this guide, you’ll create a Rails project called blog, a (very) simple weblog.如果你跟随这个指导,你将会创建一个叫做blogRails项目——一个非常简单的网络博客。Before you can start building the application, you need to make sure that you have Rails itself installed.当你准备开始构建这个项目之前你需要确保Rails已经完全安装。

3.1 Installing Rails安装Rails

In most cases, the easiest way to install Rails is to take advantage of RubyGems:在大多数情况下,最安装Rails简单的方式是通过方便的RubyGems

Usually run this as the root user:

# gem install rails#一般这样安装的rails都是最新的release版本

If youre working on Windows, you can quickly install Ruby and Rails with RailsInstaller.如果你的工作环境是Windows,你可以通过Rails Installer快速的安装Rails

/////////////////////////////

###gem help commands

~ gem install rails&&sudo gem install rails

ERROR: Could not find a valid gem ‘rails’ (>= 0) in any repository

ERROR: While executing gem (Gem::RemoteFetcher::FetchError)

Errno::ETIMEDOUT: Connection timed out connect(2) (http://rubygems.org/latest_specs.4.8.gz)

经检查和GFW无关,是rubygemsDNS 调整问题

 

问题解决的最好方法方法

jhjguxin@jhjguxin-virtual-machine:~/blog$ gem update —system

 

ERROR: gem update —system is disabled on Debian, because it will overwrite the content of the rubygems Debian package, and might break your Debian system in subtle ways. The Debian-supported way to update rubygems is through apt-get, using Debian official repositories.

 

If you really know what you are doing, you can still update rubygems by setting the REALLY_GEM_UPDATE_SYSTEM environment variable, but please remember that this is completely unsupported by Debian.

jhjguxin@jhjguxin-virtual-machine:~/blog$ export REALLY_GEM_UPDATE_SYSTEM=1

 

jhjguxin@jhjguxin-virtual-machine:~/blog$ gem updatesystem

 

###正是gem出问题了结果直接用sudo apt-get install rails,悲剧了ubuntu现在还是用的rails2.3的包然后导致,后面有些命令无法执行

3.2 Creating the Blog Application

The best way to use this guide is to follow each step as it happens, no code or step needed to make this example application has been left out, so you can literally follow along step by step.学习(使用)本指导的最好方式是跟随这里描述的每一步,不写代码或者没有例子所需的步骤会使得这个例子被冷落,你可以根据文字描述的步骤一步接着一步的操作。If you need to see the completed code, you can download it from GettingStartedCode.如果你需要完整的代码你可以从这里下载GettingStartedCode

To begin, open a terminal, navigate to a folder where you have rights to create files, and type:开始,打开一个terminal,导航至一个你有权限创建文件的文件夹,并输入:

rails new blog#—skip-bundle] # Don’t run bundle install这样在国内就不会由于连不上gem即便上能够上也会很慢半天没反映

#请确保你的rials 版本是3.1 不然的话这只能用下面的了

TheguidesforRails2.3arestillavailableathttp://guides.rubyonrails.org/v2.3.11/.

jhjguxin@jhjguxin-virtual-machine:~$ rails new blog#这里建立的文件夹名称为new额估计是新版本发生了或者有些地方没弄通(ubuntu 11.10 Rails 2.3.14 ruby 1.8.7 (2011-06-30 patchlevel 352) [i686-linux]

jhjguxin@jhjguxin-virtual-machine:~$ rails blog

jhjguxin@jhjguxin-Aspire-4750:~$ sudo gem install json -v 1.6.1

Invalid gemspec in [/var/lib/gems/1.8/specifications/tilt-1.3.3.gemspec]: invalid date format in specification: “2011-08-25 00:00:00.000000000Z”

Invalid gemspec in [/var/lib/gems/1.8/specifications/json-1.6.1.gemspec]: invalid date format in specification: “2011-09-18 00:00:00.000000000Z”

Invalid gemspec in [/var/lib/gems/1.8/specifications/tilt-1.3.3.gemspec]: invalid date format in specification: “2011-08-25 00:00:00.000000000Z”

Invalid gemspec in [/var/lib/gems/1.8/specifications/json-1.6.1.gemspec]: invalid date format in specification: “2011-09-18 00:00:00.000000000Z”

出现这个问题的时候我是改s.date=%q{2011-09-18}

rails3.1中由于要创建bundler信息(Gemfile文件)会提示你安装sqlite3等数据库信息,估计也就是你应用程序所用到的一些必须的modules

这里用的是sqlite3做数据库,提示的是安装的是sqlite3.

sudoapt-getinstallsqlite3sqlite3-doclibsqlite3-ruby

还是ERROR:Failedtobuildgemnativeextension.的话

sudoapt-getinstalllibxslt1-devlibxml2-devlibxslt-rubylibxslt1.1libxsltc-java-gcjlibxslt-ruby

libxslt1-dbglibxsltc-javaibxslthl-javalibxslt-ruby1.8

sudoapt-getinstalllibsqlite3-rubylibsqlite3-dev

sudogeminstalltherubyracer

####对于本地安装的rails(就是爬不出去墙的)sudogeminstallsqlite3-1.3.4.gem

成功了!

Yourbundleiscomplete!Usebundle</em></span></span></code><code></code><code><span style="color: #000000;"><span style="font-size: small;"><em>show</em></span></span></code><code></code><code><span style="color: #000000;"><span style="font-size: small;"><em>[gemname]toseewhereabundledgemisinstalled.

This will create a Rails application called Blog in a directory called blog.这里将创建一个名叫BlogRails应用程序在名称为blog的目录中。

You can see all of the switches that the Rails application builder accepts by running rails new -h.你可以通过运行rails new -h,查看rails应用程序创建器的所有命令(开关)。

After you create the blog application, switch to its folder to continue work directly in that application:当你创建了这个blog程序,跳转到它所在的文件夹中(直接对这个程序编辑).

$cd blog

In any case, Rails will create a folder in your working directory called blog. 在任何情况下,Rails将会创建一个名为blog的文件夹在你的工作目录中。Open up that folder and explore its contents.打开这个文件夹浏览其中的内容。Most of the work in this tutorial will happen in the app/ folder, but heres a basic rundown#概述on the function of each folder that Rails creates in a new application by default:在这个体验中的大多数的工作都是在app/这个文件夹中完成的,这里对Rails创建的新应用中的每一个文件夹的功能做出了一个概述:

File/Folder Purpose#目的
Gemfile This file allows you to specify what gem dependencies are needed for your Rails application. See section on Bundler, below.这个文件让你可以(添加)你的Rails所需要的特殊的gem依赖关系。
README This is a brief instruction manual#手册for your application. You should edit this file to tell others what your application does, how to set it up, and so on.这是一个简单的说明手册。你需要编辑这个文件告诉其他人你的应用程序可以做什么,怎么安装等等。
Rakefile This file locates and loads tasks that can be run from the command line. The task definitions are defined throughout the components of Rails. Rather than changing Rakefile, you should add your own tasks by adding files to the lib/tasks directory of your application.这个文件定位和载入能够在命令行中运行的任务。这个应该是域吧(任务定义)贯穿整个Rials的组件定义。除了修改Rakefile,你还需要添加你自己的任务的文件到你的应用程序的lib/tasks目录。
app/ Contains the controllers, models, views and assets for your application. You’ll focus on this folder for the remainder of this guide.包含controllers, models, views和你应用程序的assets(资源),再接下面的手册中你主要的注意力应该放在这里。
config/ Configure your application’s runtime rules, routes, database, and more.配置你的应用程序的运行的规则,(url)路由,数据库和其他。
config.ru Rack configuration for Rack based servers used to start the application.基于Rack服务器使用这个应用程序的Rack配置
db/ Shows your current database schema, as well as the database migrations. You’ll learn about migrations shortly.显示你当前的数据表,同样也显示数据迁移。你将会简单的了解关于(数据)迁移。
doc/ In-depth documentation for your application.应用程序的(深入)全面的文档。
lib/ Extended modules for your application (not covered in this guide).应用程序用到的扩展库(本手册没有涉及)
log/ Application log files.应用程序的日志文件
public/ The only folder seen to the world as-is. Contains the static files and compiled assets.这是外部可见的唯一文件夹。包含静态文件和编译资源。
script/ Contains the rails script that starts your app and can contain other scripts you use to deploy#部署配置or run your application.包含运行你的apprails脚本,或者其他用来配置或运行你的应用程序的scripts
test/ Unit tests, fixtures, and other test apparatus. These are covered in TestingRailsApplications单元测试,fixtures,或者其他test工具。他们在TestingRailsApplications里面有完整的讲述。
tmp/ Temporary files模板文件
vendor/ A place for all third-party code. In a typical Rails application, this includes Ruby Gems, the Rails source code (if you install it into your project) and plugins containing additional prepackaged functionality.放置第三方代码的地方。在一个典型的Rails应用程序中,这里包含Ruby GemsRails源代码(如果你把Rails安装到你的项目中)还包含一些预先包装好的额外的插件

3.3 Configuring a Database配置数据库

Just about every Rails application will interact with a database.基本上每个Rails应用程序都会和一个数据库互动。The database to use is specified in a configuration file, config/database.yml.使用的数据库在config/database.yml的配置文件中指定的。If you open this file in a new Rails application, you’ll see a default database configuration using SQLite3. The file contains sections for three different environments in which Rails can run by default:如果你在一个新的Rails应用程序中打开这个文件,你将会看到一个默认的数据库配置使用的是SQLite3.这个文件包含Rails可以默认运行的三个不同的环境会话。

  • The development environment is used on your development computer as you interact manually with the application. 开发环境使用在开发计算机上,让你和你的应用程序手动交互
  • The test environment is used to run automated tests. 测试环境用于运行自动测试
  • The production environment is used when you deploy your application for the world to use.产品环境在你向外发布过后使用
3.3.1 Configuring an SQLite3 Database

Rails comes with built-in support for SQLite3, which is a lightweight serverless database application.Rails内置并支持SQLite3SQLite3是一个轻量级的数据库服务器。While a busy production environment may overload SQLite, it works well for development and testing.但是一个繁忙的产品环境可能会导致SQLite超载,SQLite3更适合在开发或测试中使用。Rails defaults to using an SQLite database when creating a new project, but you can always change it later.Rails在创建一个新项目的时候默认使用一个SQLite数据库,但是你可以在之后随时更改。

Here’s the section of the default configuration file (config/database.yml) with connection information for the development environment:这是在开发环境中配置连接信息的默认配置文件的节选:

development:

adapter: sqlite3

database: db/development.sqlite3

pool: 5

timeout: 5000

In this guide we are using an SQLite3 database for data storage, because it is a zero configuration database that just works. 在这个手册中使用一个SQLite3数据库存存储数据,因为它不需要我们再去配置就能工作。Rails also supports MySQL and PostgreSQL “out of the box”, and has plugins for many database systems.Rails同样支持MySQLPostgreSQL ,它还有许多支持其他数据库系统的插件If you are using a database in a production environment Rails most likely has an adapter for it.如果你在产品环境中使用数据库对Rails来说仅仅是添加一个适配器而已。

3.3.2 Configuring a MySQL Database配置一个MySQL数据库

If you choose to use MySQL instead of the shipped SQLite3 database, your config/database.yml will look a little different.如果你选择MySQL代替SQLite3数据库,你的配置文件会有一点不同。Here’s the development section:这是开发环境下(的配置):

development:

adapter: mysql2

encoding: utf8

database: blog_development

pool: 5

username: root

password:

socket: /tmp/mysql.sock

If your development computer’s MySQL installation includes a root user with an empty password, this configuration should work for you. 如果你的开发计算机中安装的MySQL包含一个root用户和空的密码,这个配置文件就可以工作。Otherwise, change the username and password in the development section as appropriate.否则,修改合适的开发环境中使用的用户名和密码。

3.3.3 Configuring a PostgreSQL Database配置一个PostgreSQL数据库

If you choose to use PostgreSQL, your config/database.yml will be customized to use PostgreSQL databases:

development:

adapter: postgresql

encoding: unicode

database: blog_development

pool: 5

username: blog

password:

3.3.4 Configuring an SQLite3 Database for JRuby Platform配置在JRuby平台中使用的SQLite3数据库

If you choose to use SQLite3 and using JRuby, your config/database.yml will look a little different. Here’s the development section:

development:

adapter: jdbcsqlite3

database: db/development.sqlite3

3.3.5 Configuring a MySQL Database for JRuby Platform配置在JRuby平台中使用的MySQL数据库

If you choose to use MySQL and using JRuby, your config/database.yml will look a little different. Here’s the development section:

development:

adapter: jdbcmysql

database: blog_development

username: root

password:

3.3.6 Configuring a PostgreSQL Database for JRuby Platform配置在JRuby平台中使用的PostgreSQL数据库

Finally if you choose to use PostgreSQL and using JRuby, your config/database.yml will look a little different. Here’s the development section:

development:

adapter: jdbcpostgresql

encoding: unicode

database: blog_development

username: blog

password:

Change the username and password in the development section as appropriate.修改在开发会话中使用的合适的用户名和密码

You don’t have to update the database configurations manually. 你不是必须手动的配置数据库If you look at the options of the application generator, you will see that one of the options is named database. 如果你查看应用程序产生器,你将会发现一个选项(-d, —database=name Preconfigure for selected database (options: mysql/oracle/postgresql/sqlite2/sqlite3/frontbase/ibm_db).

This option allows you to choose an adapter from a list of the most used relational databases. You can even run the generator repeatedly: cd .. && rails blog —database=mysqlWhen you confirm确认the overwriting of the config/database.yml file, your application will be configured for MySQL instead of SQLite.

3.4 Creating the Database创建数据库

Now that you have your database configured,it’s time to have Rails create an empty database for you. 现在你已经有了你的数据库配置文件了,是时候创建一个空的数据库了。You can do this by running a rake command:

$ rake db:create #确保你的系统中已经有一个JavaScript runtime.#可能还需要执行sudo geminstalltherubyracer&&sudo gem install execjs

jhjguxin@jhjguxin-virtual-machine:~/blog$ rake db:create

rake aborted!

Could not find a JavaScript runtime. See https://github.com/sstephenson/execjs for a list of available runtimes.

one way:

并且In your Gemfile

write this

gem 'execjs'

gem 'therubyracer'

and then run

bundle install

#ubuntu环境中安装sqlite3sudoapt-getinstalllibsqlite3-rubylibsqlite3-dev 也尝试过使用gem install sqlite3-ruby但是网上说是gem nds有问题安装ruby mysqlsudoapt-getinstalllibmysql-ruby

another way:

参照上面的网址:我找了一个ubuntu里面有的Node.js就可以不用那个therubyracer

jhjguxin@jhjguxin-virtual-machine:~/blog$ rake db:create

This will create your development and test SQLite3 databases inside the db/ folder.将会在db/文件夹中创建你的开发环境的数据库文件development.sqlite3

Rake is a general-purpose command-runner that Rails uses for many things. You can see the list of available rake commands in your application by running rake -T.Rake是一个通用的命令行可以帮助Rails用户完成很多事情。你可以通过运行rake -T 查看可用的rake命令

ruby script/server#或者script/server

4 Hello, Rails!

One of the traditional places to start with a new language is by getting some text up on screen quickly. To do this, you need to get your Rails application server running.传统的方式之一,开始使用一种新的(命令)语法并得到快速掠过的文字。要得到这样的结果你需要使你的Rails程序运行。

$ rails server #——-rails2.3.1 $ ruby script/server#

This will fire up an instance of the WEBrick web server by default (Rails can also use several other web servers). 这里默认将开启一个WEBrick服务器的的实例(Rails也可能使用一些其他的web服务器)。To see your application in action, open a browser window and navigate to http://localhost:3000. You should see Rails’ default information page:查看你的应用程序的行为,打开一个浏览器并且导航到127.0.0.03000你将会看到一个Rails默认的信息页面。

To stop the web server, hit Ctrl+C in the terminal window where it’s running. In development mode, Rails does not generally require you to stop the server; changes you make in files will be automatically picked up by the server.要终止web服务,在命令运行的终端中按下Ctrl+C。在开发环境模式中,Rails一般不需要你停止服务;你所做的更改将自动的编译进需要的文件中并且重启服务。

The “Welcome Aboard#page is the smoke test for a new Rails application: it makes sure that you have your software configured correctly enough to serve a page.这个欢迎界面体现了一个新的Rails应用程序创建成功(通过了Rails的自检)。You can also click on the About your applications environment link to see a summary of your application’s environment.你可以点击About your applications environment查看你的应用程序运行环境摘要信息。

4.2 Say “Hello”, Rails

To get Rails saying “Hello”, you need to create at minimum a controller and a view.要使Rails说出(显示)你好,你还需要创建一个最小的a controller and a viewFortunately, you can do that in a single command. Enter this command in your terminal:幸运的是,你可以完成这些通过一行命令。在终端中输入: rails generate controller home index ##rails2.3.1 script/generatecontrollerhomeindex##

If you get a command not found error when running this command, you need to explicitly pass Rails rails commands to Ruby: ruby \path\to\your\application\script\rails generate controller home index.如果你在输入这个命令的时候出现没有这个命令错误,你需要明确的使用ruby来执行Rails命令。这里就没有rails这个文件应该是rubyscript/generatecontrollerhomeindex

Rails will create several files for you, including app/views/home/index.html.erb. Rails将会为你创建一些文件,包含app/views/home/index.html.erbThis is the template that will be used to display the results of the index action (method) in the home controller.这个模板会用来显示在home controller中的index action (method)的结果。Open this file in your text editor and edit it to contain a single line of code:在文本编辑器中打开这个文件并输入:

<h1>Hello, Rails!</h1>

4.3 Setting the Application Home Page

Now that we have made the controller and view, we need to tell Rails when we want “Hello Rails” to show up. 现在我们已经创建了controller view,我们还需要告诉Rails我们想在什么时候显示出来。In our case, we want it to show up when we navigate to the root URL of our site, http://localhost:3000, instead of the “Welcome Aboard” smoke test.在本例中,我们想让它在我们导航至站点url根目录的时候替代Welcome Aboard”显示。

The first step to doing this is to delete the default page from your application:首先移除应用程序中的默认页面。

rm public/index.html

We need to do this as Rails will deliver#提供,传输any static file in the public directory in preference#偏好优先to any dynamic#动态content we generate from the controllers.我们必须这样做因为,Rails将会传送任何在public的静态文件优先于我们在controllers生成的动态(显示)内容。

Now, you have to tell Rails where your actual home page is located.Open the file config/routes.rb in your editor. This is your application’s routing file which holds entries in a special DSL (domain-specific language) that tells Rails how to connect incoming requests to controllers and actions. This file contains many sample routes on commented lines, and one of them actually shows you how to connect the root of your site to a specific controller and action. Find the line beginning with root :to, uncomment it and change it like the following:现在你还必须告诉Rails 你实际上的主页在哪里。在文本编辑器中打开config/routes.rb 。这是你应用程序的路由文件,它采用DSL语言囊括了告诉Rails怎样连接请求信息到controllers and actions.的所有条目。

这个文件包含许多简单的路由命令,其中一条实际上是用于告诉我们怎样连接你站点根目录到一个指定的controller and acti。找到以root :to开头的那一行,注释掉它改成如下内容:

Blog::Application.routes.draw do

 

get “home/index”

 

root :to => “home#index”

//////////////////this depend on rails 2.3.1/////////

Blog::Application.routes.draw do

 

#…

# You can have the root of your site routed with “root”

# just remember to delete public/index.html.

map.root :controller => “home”

or

map.root :controller => “home”,:action => ‘index’

##root :to => “home#index”

The root :to => “home#index” tells Rails to map the root action to the home controller’s index action.

ps-ef|grepwebrick

kill-9上面得到的id强制结束WEBrick服务

Now if you navigate to http://localhost:3000 in your browser, you’ll see Hello, Rails!.现在你在浏览器中导航至http://localhost:3000 ,你将会看到Hello, Rails!”.

For more information about routing, refer to RailsRoutingfromtheOutsideIn.更多的信息请参见RailsRoutingfromtheOutsideIn

5 Getting Up and Running Quickly with Scaffolding使用Scaffolding快速创建并运行

Rails scaffolding is a quick way to generate some of the major pieces of an application. Rails scaffolding是一个快速的方法产生应用程序的一些主要的部分。If you want to create the models, views, and controllers for a new resource in a single operation, scaffolding is the tool for the job.如果你想使用一种简单的操作为新资源创建models,viewscontrollersScaffolding是一个不错的工具。

6 Creating a Resource创建一个资源

In the case of the blog application, you can start by generating a scaffolded Post resource: this will represent#代表,表现表示a single blog posting. To do this, enter this command in your terminal:在本示例中的blog应用程序,你可以使用scaffolded产生post资源:它表现为一个简单的blog posting。要完成这些,在终端输入如下命令:

$ rails generate scaffold Post name:string title:string content:text

rails 2.3.1 script/generate scaffold Post name:string title:string content:text

The scaffold generator will build several files in your application, along with some folders, and edit config/routes.rb. Here’s a quick overview of what it creates:scaffold创建器将会在应用程序中的一些文件夹中生成一些文件,并且还会编辑config/routes.rb。下面这些产生的文件的大概说明:

File Purpose
db/migrate/20100207214725_create_posts.rb Migration to create the posts table in your database (your name will include a different timestamp)将创建的posts表单迁移到你的数据库(会在你的命名前面加上时间)
app/models/post.rb The Post model Post模型
test/fixtures/posts.yml Dummy posts for use in testing 模拟测试post
app/controllers/posts_controller.rb The Posts controller
app/views/posts/index.html.erb A view to display an index of all posts 显示所有post
app/views/posts/edit.html.erb A view to edit an existing post编辑post
app/views/posts/show.html.erb A view to display a single post显示一条post
app/views/posts/new.html.erb A view to create a new post创建post
app/views/posts/_form.html.erb A partial to control the overall look and feel of the form used in edit and new views一个用于控制编辑和创建新视图的整体视效表
app/helpers/posts_helper.rb Helper functions to be used from the post views使用post的帮助功能views
app/assets/stylesheets/scaffolds.css.scss Cascading style sheet#层叠样式to make the scaffolded views look better #css使scaffolded视图具有更好的效果
app/assets/stylesheets/posts.css.scss Cascading style sheet for the posts controller#post controllercss效果
app/assets/javascripts/posts.js.coffee CoffeeScript for the posts controller
test/unit/post_test.rb Unit testing harness#利用for the posts model post modelsUnit测试
test/functional/posts_controller_test.rb Functional testing harness for the posts controller post controller的功能测试
test/unit/helpers/posts_helper_test.rb Unit testing harness for the posts helper post helperUint 测试
config/routes.rb Edited to include routing information for posts 加入posts 路由信息

While scaffolding will get you up and running quickly, the code it generates is unlikely to be a perfect fit for your application.即便是scaffolding使你创建和运行非常快捷,但是产生的代码不可能完美的适合你的应用程序。Youll most probably want to customize the generated code.你大多数都需要定制产生的代码。Many experienced Rails developers avoid scaffolding entirely, preferring to write all or most of their source code from scratch. 很多有经验的Rails开发人员完全不使用scaffolding,宁愿从头编写全部的代码。Rails, however, makes it really simple to customize templates for generated models, controllers, views and other source files.Rails,无论如何,使得为生成的modelscontrollersviews或者其他代码编定制模板非常简单。Youll find more information in the CreatingandCustomizingRailsGenerators&Templates guide.你可以在CreatingandCustomizingRailsGenerators&Templates看到更多信息。

6.1 Running a Migration

One of the products of the rails generate scaffold command is a database migration. Migrations are Ruby classes that are designed to make it simple to create and modify database tables. Rails uses rake commands to run migrations, and it’s possible to undo a migration after it’s been applied to your database. Migration filenames include a timestamp to ensure that they’re processed#处理in the order that they were created.script/generate scaffold的一个产品就是数据迁移。Migrations是一个ruby类被设计用来使数据库表单的创建和修改变得简单。Rails使用rake命令来执行迁移,它还可以撤销已经应用的修改。Migration filenames include a timestamp确保了迁移能够完成。

If you look in the db/migrate/20100207214725_create_posts.rb file (remember, yours will have a slightly different name记住,你得到的可能会有略微不同), here’s what you’ll find:

class CreatePosts < ActiveRecord::Migration

def change

create_table :posts do |t|

t.string :name

t.string :title

t.text :content

 

t.timestamps

end

end

end

The above migration creates a method name change which will be called when you run this migration.整个migration创建了一个名叫change的方法,该方法在你运行这个migration的时候被调用。The action defined in that method is also reversible#可逆, which means Rails knows how to reverse the change made by this migration, in case you want to reverse it at later date.这个方法中定义的行为也是可逆的,那就是说Rails知道怎样逆向改变这个migration,如果你需要恢复到上一次数据。By default, when you run this migration it will creates a posts table with two string columns and a text column. 默认情况下,当你运行这个migration,他将会创建一个包含两个字符串列和一个text列的表单。It also creates two timestamp fields to track record creation and updating. More information about Rails migrations can be found in the RailsDatabaseMigrations guide.关于Rails migration的更多信息请阅读RailsDatabaseMigrations手册。

At this point, you can use a rake command to run the migration:这个时候,你可以使用rake命令运行migration了:

rake db:migrate

Rails will execute this migration command and tell you it created the Posts table.Rails将会执行这个migration命令并且通知你它创建了Post表单。

== CreatePosts: migrating ====================================================

create_table(:posts)

–> 0.0030s

== CreatePosts: migrated (0.0032s) ===========================================

Because youre working in the development environment by default, this command will apply to the database defined in the development section of your config/database.yml file. If you would like to execute migrations in other environment, for instance in production, you must explicitly pass it when invoking the command: rake db:migrate RAILS_ENV=production.由于你默认工作在开发环境中,这个命令将会应用于开发环境会话的数据库位于你的config/database.yml 中。如果你想执行migration在其他环境中,比如以产品(环境)为实例,你必须明确调用的通过命令行中执行:rake db:migrate RAILS_ENV=production

6.2 Adding a Link添加要给link(到blog

To hook the posts up to the home page you’ve already created, you can add a link to the home page.把你已经创建好的post挂到主页上,你可以通过添加一个link到主页。Open app/views/home/index.html.erb and modify it as follows:打开app/views/home/index.html.erb并且按照下面所示更改:

<h1>Hello, Rails!</h1>

<%= link_to “My Blog”, posts_path %>

The link_to method is one of Railsbuilt-in view helpers.这个链接方法是Railsview helpers 的内建方法之一It creates a hyperlink based on text to display and where to goin this case, to the path for posts.它创建一个基于文字的超级链接并显示到哪里,在这个实例中(跳转)到posts

6.3 Working with Posts in the Browser

Now you’re ready to start working with posts. To do that, navigate to http://localhost:3000 and then click the “My Blog” link:现在你已经准备好在posts中工作了。导航至http://localhost:3000,并且点击My Blog”链接。

This is the result of Rails rendering the index view of your posts.这就是Rails渲染你的posts视图的结果。There arent currently any posts in the database, but if you click the New Post link you can create one.在你点击New Post链接并创建一个新的post之前,数据库里面是没有任何post的。After that, youll find that you can edit posts, look at their details, or destroy them. 随后你可以编辑,查看详细内容,或者删除他们。All of the logic and HTML to handle this was built by the single rails generate scaffold command.post的所有的logicHTML都是通过rails generate scaffold 生成的。

In development mode (which is what youre working in by default), Rails reloads your application with every browser request, so theres no need to stop and restart the web server.在开发模式中(你的默认工作模式),Rails会在每个浏览器请求的时候重新载入你的应用程序,因此你不需要停止或者重启web服务。

Congratulations, youre riding the rails! Now its time to see how it all works.恭喜,你已经驯服了rails!现在是时候去看看它的所有工作了。

6.4 The Model

The model file, app/models/post.rb is about as simple as it can get:

class Post < ActiveRecord::Base

end

There isnt much to this filebut note that the Post class inherits from ActiveRecord::Base.这里有可能不一致——但是切记Post类继承于ActiveRecord::BaseActive Record supplies a great deal of functionality to your Rails models for free, including basic database CRUD (Create, Read, Update, Destroy) operations, data validation, as well as sophisticated#复杂search support and the ability to relate multiple models to one another. Active Record免费为你的models提供了强大的功能,包括基本数据库的CRUD(创建,读取,更新,删除)操作,数据验证,以及复杂的的查询与其它数据表单多关联的字段的支持能力。

6.5 Adding Some Validation添加一些验证

Rails includes methods to help you validate the data that you send to models. Rails包含一些帮助你验证发送到models的数据的方法。Open the app/models/post.rb file and edit it:打开app/models/post.rb并编辑:

class Post < ActiveRecord::Base

validates :name, :presence => true

validates :title, :presence => true,

:length => { :minimum => 5 }

end

These changes will ensure that all posts have a name and a title, and that the title is at least five characters long. 这些更改会确保所有的post都有一个nametitile并且title长度至少五个字符。Rails can validate a variety of各种,很多conditions#名词(字段)in a model, including the presence or uniqueness独特of columns, their format, and the existence of associated相关objects. Rails可以验证很多种字段,比如字段能否为空和独特性,字段的格式,以及字段的关联。

6.6 Using the Console

To see your validations in action, you can use the console.要想在action里面查看你的验证你可以使用consoleThe console is a command-line tool that lets you execute#执行运行Ruby code in the context of your application:console是一个可以让你在你的应用程序的上下文中执行Ruby代码的命令行工具:

$ rails console

The default console will make changes to your database.默认的console将会改变你的数据库。You can instead open a console that will roll back any changes you make by using rails console —sandbox.你可以通过运行rails consolesandbox or rails console -s,这样你可以回滚你的任何操作。

jhjguxin@jhjguxin-Aspire-4750:~/blog$ rails console -s

Loading development environment in sandbox (Rails 3.1.1)

Any modifications you make will be rolled back on exit

irb(main):001:0> p=Post.new(:content=>“A new post to test console”)

=> #<Post id: nil, name: nil, title: nil, content: “A new post to test console”, created_at: nil, updated_at: nil>

irb(main):002:0> p.save

(0.2ms) SAVEPOINT active_record_1

(0.1ms) ROLLBACK TO SAVEPOINT active_record_1

=> false

irb(main):003:0> p.errors

=> #<ActiveModel::Errors:0xb6765d48 @base=#<Post id: nil, name: nil, title: nil, content: “A new post to test console”, created_at: nil, updated_at: nil>, @messages=#<OrderedHash {:title=>[“is too short (minimum is 5 characters)”, “can’t be blank”], :name=>[“can’t be blank”]}>>

This code shows creating a new Post instance, attempting to save it and getting false for a return value (indicating that the save failed), and inspecting the errors of the post.这段代码演示了创建一个Post实例,并企图保存到数据库并得到一个失败的返回值(说明保存失败的原因),检查post的错误信息。

When you’re finished, type exit and hit return to exit the console.当你操作完成,输入exit并回车退出console

Unlike the development web server, the console does not automatically load your code afresh for each line.不像开发环境的web服务器console不会自动导入你每行输入的新的代码。If you make changes to your models while the console is open, type reload! at the console prompt to load them.如果你改变了你的moels并且console是打开的,输入reload!那么console会立即导入他们。

jhjguxin@jhjguxin-Aspire-4750:~/blog$ rails console

Loading development environment (Rails 3.1.1)

irb(main):001:0> p=Post.new(:title=>“test console”,:name=>“jhjguxin”,:content=>“A new post to test console”)

=> #<Post id: nil, name: “jhjguxin”, title: “test console”, content: “A new post to test console”, created_at: nil, updated_at: nil>

irb(main):002:0> p.save

SQL (13.4ms) INSERT INTO “posts” (“content”, “created_at”, “name”, “title”, “updated_at”) VALUES (?, ?, ?, ?, ?) [[“content”, “A new post to test console”], [“created_at”, Sat, 05 Nov 2011 15:55:17 UTC +00:00], [“name”, “jhjguxin”], [“title”, “test console”], [“updated_at”, Sat, 05 Nov 2011 15:55:17 UTC +00:00]]

=> true

irb(main):003:0> reload!

Reloading…

=> true

###如果是rails console —sandbox could not reload! Successfully.

6.7 Listing All Posts

The easiest place to start looking at functionality is with the code that lists all posts. 寻找所有功能的地方是使用代码列出所有的postOpen the file app/controllers/posts_controller.rb and look at the index action:打开文件app/controllers/posts_controller.rb,并且查看index action

def index

@posts = Post.all

 

respond_to do |format|

format.html # index.html.erb

format.json { render :json => @posts }

end

end

Post.all calls the Post model to return all of the posts currently in the database. Post.all调用Post model并返回当前在数据库中的所有postThe result of this call is an array of posts that we store in an instance variable called @posts.本次调用的结果是一个post的数组,并且我们将这个数组存储在一个叫做@posts的实例变量中。

For more information on finding records with Active Record, see ActiveRecordQueryInterface.查找有关Active Record更多的信息,可以查看ActiveRecordQueryInterface相关记录。

The respond_to block handles both HTML and JSON calls to this action.这个respond_to块处理了这个动作的HTMLJSON请求。If you browse to http://localhost:3000/posts.json, you’ll see a JSON containing all of the posts.如果你浏览http://localhost:3000/posts.json,你将会看到一个JSON包含着所有的postThe HTML format looks for a view in app/views/posts/ with a name that corresponds to the action name. 这个HTML格式在app/views/posts/view中查找相对应的动作名称。Rails makes all of the instance variables from the action available to the view.Rails使来自action的所有的实例变量对应到(可用)viewHere’s app/views/posts/index.html.erb:

<h1>Listing posts</h1>

 

<table>

<tr>

<th>Name</th>

<th>Title</th>

<th>Content</th>

<th></th>

<th></th>

<th></th>

</tr>

 

<% @posts.each do |post| %>

<tr>

<td><%= post.name %></td>

<td><%= post.title %></td>

<td><%= post.content %></td>

<td><%= link_to ‘Show’, post %></td>

<td><%= link_to ‘Edit’, edit_post_path(post) %></td>

<td><%= link_to ‘Destroy’, post, :confirm => ‘Are you sure?’, :method => :delete %></td>

</tr>

<% end %>

</table>

 

<br />

 

<%= link_to ‘New post’, new_post_path %>

This view iterates#迭代over the contents of the @posts array to display content and links.这个view迭代@posts数组所有的内容并显示相关的内容和链接。A few things to note in the view:关于视图note一些信息:

  • link_to builds a hyperlink to a particular destination link_to创建一个超链接到一个地方
  • edit_post_path and new_post_path are helpers that Rails provides as part of RESTful routing.edit_post_path and new_post_pathRails提供的RESTful路由向导。You’ll see a variety of these helpers for the different actions that the controller includes. 不同的具有controlleractions中你将会看到一系列的这样的向导。

In previous versions of Rails, you had to use <%=h post.name %> so that any HTML would be escaped before being inserted into the page.在以前的版本的Rails中,你必须使用<%=h post.name %> 以避免一些HTML可能会转义在插入到页面之前。In Rails 3.0, this is now the default.Rails 3.0,作为默认。To get unescaped HTML, you now use <%= raw post.name %>.得到一个非转义的HTML,你现在使用<%=raw post.name%>.

For more details on the rendering process, see LayoutsandRenderinginRails.了解更过关于渲染处理流程,阅读LayoutsandRenderinginRails.

6.8 Customizing the Layout定制布局

The view is only part of the story of how HTML is displayed in your web browser.view仅仅告诉HTML要显示什么(内容)在你的web浏览器里面。Rails also has the concept of layouts, which are containers for views.Rails也有关于布局的概念(定义),那就是(布局)是对viewi的包装。When Rails renders a view to the browser, it does so by putting the view’s HTML into a layout’s HTML.Rails渲染一个view到浏览器,它通常是(这样做)把viewHTML放进布局的HTML中。In previous versions of Rails, the rails generate scaffold command would automatically create a controller specific layout, like app/views/layouts/posts.html.erb, for the posts controller. 在以前的版本中,rails generate scaffold 命令将会自动创建controller对应指定的布局。However this has been changed in Rails 3.0. An application specific layout is used for all the controllers and can be found in app/views/layouts/application.html.erb. 然而在rails3.0中有所不同了。一个应用程序指定的布局适用于所有的controllers,可以在app/views/layouts/application.html.erb 中找到(这就好像是djangobase.html)Open this layout in your editor and modify the body tag:打开这个layout在你的编辑器中并且修改body标签:

<!DOCTYPE html>

<html>

<head>

<title>Blog</title>

<%= stylesheet_link_tag “application” %>

<%= javascript_include_tag “application” %>

<%= csrf_meta_tags %>

</head>

<body style=“background: #EEEEEE;”>

 

<%= yield %>

 

</body>

</html>

Now when you refresh the /posts page, you’ll see a gray background to the page. This same gray background will be used throughout all the views for posts.现在你刷新/posts页面,你将会看到一个灰色的页面背景。并且相同的灰色背景将会使用在posts的所有视图中。

6.9 Creating New Posts

Creating a new post involves two actions. The first is the new action, which instantiates an empty Post object:创建一个new post 包含两个actions。首先是new action,它会实例化一个空的Post对象:

def new

@post = Post.new

 

respond_to do |format|

format.html # new.html.erb

format.json { render :json => @post }

end

end

The new.html.erb view displays this empty Post to the user: 这个new.html.erb视图显示这个空的post给用户:

<h1>New post</h1>

 

<%= render ‘form’ %>

 

<%= link_to ‘Back’, posts_path %>

The <%= render ‘form’ %> line is our first introduction to partials in Rails.<%= render ‘form’ %>是我们第一个介绍的RailspartialsA partial is a snippet of HTML and Ruby code that can be reused in multiple locations.一个partial是一个HTML代码片段和Ruby代码的组合可以在多目标对象中重用。(类似于djangoinclude ‘other.html’In this case, the form used to make a new post, is basically identical to a form used to edit a post, both have text fields for the name and title and a text area for the content with a button to make a new post or update the existing post.在本例中,form用于创建new post,它相当于一个用于编辑一个post的表单,这个表单有一个name text fields title text fields 以及一个contenttext area还有一个用于添加一个新的post或者更新已经存在的post的按钮。

If you take a look at views/posts/form.html.erb file, you will see the following:如果你看一下views/posts/form.html.erb这个文件,你将会发现下面的内容:

<%= form_for(@post) do |f| %>

<% if @post.errors.any? %>

<div id=“error_explanation”>

<h2><%= pluralize(@post.errors.count, “error”) %> prohibited this post from being saved:</h2>

<ul>

<% @post.errors.full_messages.each do |msg| %>

<li><%= msg %></li>

<% end %>

</ul>

</div>

<% end %>

<div>

<%= f.label :name %><br />

<%= f.text_field :name %>

</div>

<div>

<%= f.label :title %><br />

<%= f.text_field :title %>

</div>

<div>

<%= f.label :content %><br />

<%= f.text_area :content %>

</div>

<div>

<%= f.submit %>

</div

<% end %>

This partial receives all the instance variables defined in the calling view file, so in this case, the controller assigned the new Post object to @post and so, this is available in both the view and partial as @post.这个partial接收在view文件中定义的所有的实例变量,因此在本例中,controller申请新的Post对象给@post@postviewipartial都是可用的。

For more information on partials, refer to the LayoutsandRenderinginRails guide.有关partials的更多信息,参考LayoutsandRenderinginRails指导。

The form_for block is used to create an HTML form.form_for代码块用于创建一个HTML表单。Within this block, you have access to methods to build various controls on the form. 在这个代码块中你可以在访问方法的基础上在表单上创建各种控制。For example, f.text_field :name tells Rails to create a text input on the form, and to hook it up to the name attribute of the instance being displayed.比如,f.test_field :name 告诉Rails在表单中创建一个text inputYou can only use these methods with attributes of the model that the form is based on (in this case name, title, and content).form使用的方法基于model的相对应的字段属性(类型如text_field or text_area)(例如本例中的name,title,content)。Rails uses form_for in preference偏好优先to having you write raw HTML because the code is more succinct#简洁, and because it explicitly明确ties关系the form to a particular model instance.Rails使用(偏向于使用)form_for列出你要输入的HTML行因为这样代码更加简洁,并却这样使得formparticular model实例关系更加明显。

The form_for block is also smart enough to work out if you are doing a New Post or an Edit Post action, and will set the form action tags and submit button names appropriately in the HTML output.form_for代码块同样也足够你定制你的New PostEdit Post action,并且将会设置form action标签和在HTML输出中显示的提交按钮名称。

If you need to create an HTML form that displays arbitrary#任意fields, not tied to a model, you should use the form_tag method, which provides shortcuts for building forms that are not necessarily tied to a model instance.如果你需要创建一个HTML表单显示任意的域,而不绑定到model字段中,你应该使用form_tag方法,它快捷的保证了建立forms不必在绑定到一个model实例。

When the user clicks the Create Post button on this form, the browser will send information back to the create method of the controller (Rails knows to call the create method because the form is sent with an HTTP POST request; thats one of the conventions that I mentioned earlier):当用户点击这张表单上面的创建Post按钮,浏览器将会发送信息——controller的方法回服务器(Rails知道调用create方法,因为form是以HTTP POST请求发送,这是我随后提到的一种协议之一)

def create

@post = Post.new(params[:post])

respond_to do |format|

if @post.save

format.html { redirect_to @post, :notice => ‘Post was successfully created.’ }

format.json { render :json => @post, :status => :created, :location => @post }

else

format.html { render :action => “new” }

format.json { render :json => @post.errors, :status => :unprocessable_entity }

end

end

end

The create action instantiates a new Post object from the data supplied by the user on the form, which Rails makes available in the params hash.create action实例化一个新的Post对象,这个对象给form提供数据支持。After successfully saving the new post, create returns the appropriate format that the user has requested (HTML in our case).当成功的保存了新postcreate返回用户请求的适当的格式(在本例中是HTML)。It then redirects the user to the resulting post show action and sets a notice to the user that the Post was successfully created.然后重定向用户页面到结果显示的post show action页面并且给出提示Post成功的创建了。

If the post was not successfully saved, due to a validation error, then the controller returns the user back to the new action with any error messages so that the user has the chance to fix the error and try again.如果post没有保存成功,是因为(数据)验证错误,然后controller控制用户页面回到new action(包含验证错误新息)给用户。

The “Post was successfully created.” message is stored inside of the Rails flash hash, (usually just called the flash) so that messages can be carried#over to another action, providing the user with useful information on the status of their request. “Post was successfully created.” 这条消息被存储在Railsflashhash表中,(通常之叫它flash)因此消息可以转载到另一个action,在请求状态中提供有用的信息给用户。In the case of create, the user never actually sees any page rendered during the Post creation process, because it immediately redirects to the new Post as soon Rails saves the record.在这个新建例子(数据验证失败)中,用户实际上从来不看任何在页面创建进程中的渲染页面,因为它立刻重定向页面到new PostRails保存了这个记录。The Flash carries over a message to the next action, so that when the user is redirected back to the show action, they are presented with a message saying “Post was successfully created.”Flash装载消息到接下来的action,因此当用户被重定向到了show action,他们立刻收到了一条消息Post was successfully created.”

6.10 Showing an Individual Post显示一条单个的Post

When you click the show link for a post on the index page, it will bring you to a URL like http://localhost:3000/posts/1. 当你在posts 的主页面点击一个postshow这个超链接,他将会产生一个url http://localhost:3000/posts/1Rails interprets解释this as a call to the show action for the resource, and passes in 1 as the :id parameter.Rails解释这是一个到show actionresource 调用。Here’s the show action:这里是show action

def show

@post = Post.find(params[:id])

respond_to do |format|

format.html # show.html.erb

format.json { render :json => @post }

end

end

The show action uses Post.find to search for a single record in the database by its id value.这里的show action使用Post.find通过对应记录的id来查找单个记录。After finding the record, Rails displays it by using show.html.erb:当找到记录,Rails使用show.html.erb来显示它:

<p id=“notice”><%= notice %></p>

<p>

<b>Name:</b>

<%= @post.name %>

</p>

<p>

<b>Title:</b>

<%= @post.title %>

</p>

<p>

<b>Content:</b>

<%= @post.content %>

</p>

<%= link_to ‘Edit’, edit_post_path(@post) %> |

<%= link_to ‘Back’, posts_path %>

6.11 Editing Posts编辑Posts

Like creating a new post, editing a post is a two-part process.类似创建一个新的post,编辑一个post也(分为)两部分。The first step is a request to edit_post_path(@post) with a particular post. This calls the edit action in the controller:首先是到edit_post_path(@post)请求一个特定的post。这里是调用的在controller中的edit action

def edit

@post = Post.find(params[:id])

end

After finding the requested post, Rails uses the edit.html.erb view to display it:在找到了请求的postRails使用edit.html.erb试图来显示它:

<h1>Editing post</h1>

 

<%= render ‘form’ %>

 

<%= link_to ‘Show’, @post %> |

<%= link_to ‘Back’, posts_path %>

Again, as with the new action, the edit action is using the form partial, this time however, the form will do a PUT action to the PostsController and the submit button will displayUpdate Post再一次的,就像new actionedit action也使用form部分,这次有所不同,form将会提交一个PUT actionPostsController并且提交按钮将会显示Update Post

Submitting the form created by this view will invoke调用the update action within the controller:提交的form由上面这个视图创建的并且还会调用controller中的update action

def update

@post = Post.find(params[:id])

respond_to do |format|

if @post.update_attributes(params[:post])

format.html { redirect_to @post, :notice => ‘Post was successfully updated.’ }

format.json { head :ok }

else

format.html { render :action => “edit” }

format.json { render :json => @post.errors, :status => :unprocessable_entity }

end

end

end

In the update action, Rails first uses the :id parameter passed back from the edit view to locate the database record thats being edited.update action中,Rails首先使用:id参数从edit view(传值到)数据库记录下刚才编辑的内容。The update_attributes#更新model的属性call then takes the rest of the parameters from the request and applies them to this record. update_attributes在应用一些(更多)参数的来自request的数据到recode时被调用。If all goes well, the user is redirected to the posts show view. 如果一切成功,用户会被重定向到postshow视图。If there are any problems, its back to the edit view to correct them.如果期间发生了任何错误,它将回到edit视图并(要求)改正他们。

6.12 Destroying a Post 摧毁一个post

Finally, clicking one of the destroy links sends the associated id to the destroy action:最后,点击一个destroy链接发送相关的iddestroy动作:

def destroy

@post = Post.find(params[:id])

@post.destroy

 

respond_to do |format|

format.html { redirect_to posts_url }

format.json { head :ok }

end

end

The destroy method of an Active Record model instance removes the corresponding record from the database.这个destroyActive Recordmodel的实例(功能是)从数据库中移除相应的记录。After that’s done, there isn’t any record to display, so Rails redirects the user’s browser to the index view for the model.当这个(操作)完成,这里没有任何记录显示,因此Rails重定向用户的浏览器到model的主页视图。

7 Adding a Second Model添加第二个Modelcomment

Now that you’ve seen how a model built with scaffolding looks like, it’s time to add a second model to the application.你已经知道了通过scaffolding生成的model看起来是怎样的。The second model will handle comments on blog posts.第二个model用来处理blog post的评论。

7.1 Generating a Model构造一个model

Models in Rails use a singular name, and their corresponding database tables use a plural name. Rails 中的Models使用一个单数名称,同时它们相关的数据库表使用一个复数名称。For the model to hold comments, the convention is to use the name Comment.对于评论在models中的代名词,习惯上使用的的是CommentEven if you don’t want to use the entire apparatus set up by scaffolding, most Rails developers still use generators to make things like models and controllers.即使你不想完完全全的使用scaffolding,大多数的Rails仍然使用生成器来做这些事情比如modelscontrollersTo create the new model, run this command in your terminal:要创建一个新的model,在终端中运行下面这条命令:

rails generate model Comment commenter:string body:text post:references #references引用

This command will generate four files:这条命令将会生成四个文件:

  • app/models/comment.rb – The model 模型
  • db/migrate/20111108080402_create_comments.rb – The migration 数据迁移
  • test/unit/comment_test.rb andtest/fixtures/comments.yml – The test harness. 测试工具

First, take a look at comment.rb:首先,看一看comment.rb:

class Comment < ActiveRecord::Base

belongs_to :post

end

This is very similar to the post.rb model that you saw earlier.这和你刚刚看到post.rb很近似。The difference is the line belongs_to :post, which sets up an Active Record association. 不同的是这行belongs_to :post,他会安装一个Active Record associationYou’ll learn a little about associations in the next section of this guide.你将会在接下来的guide学习一点有关associations的内容。

In addition to the model, Rails has also made a migration to create the corresponding database table:除了模型,Rails同样也产生了一个migration来创建相应的数据库表单:

class CreateComments < ActiveRecord::Migration

def change

create_table :comments do |t|

t.string :commenter

t.text :body

t.references :post

t.timestamps

end

add_index :comments, :post_id

end

end

The t.references line sets up a foreign key column for the association between the two models.对于t.references这行,会在两个models之间生成一个外键列从而形成一个关系(组合)。And the add_index line sets up an index for this association column. Go ahead and run the migration:而且add_index line生成一个首页关联到这个关系行:

$ rake db:migrate

Rails is smart enough to only execute the migrations that have not already been run against the current database, so in this case you will just see:Rails能够智能的只针对对没有被运行过的表单,执行migrations生成当前的数据库,因此这里你只会看到:

7.2 Associating Models关联models

Active Record associations let you easily declare the relationship between two models. In the case of comments and posts, you could write out the relationships this way:Active Record associations让你很容易的申明两个models之间的关系。在本例中的commentsposts,你可以写出这样描述关系:

  • Each comment belongs to one post 一条comment对应于一个post
  • One post can have many comments 一个post可以对应于多个comments

In fact, this is very close to the syntax that Rails uses to declare this association.实际上,这也很接近Rails申明的association 的语法。You’ve already seen the line of code inside the Comment model that makes each comment belong to a Post:你已经看到了在Comment model中的使每个comment对应于一个post的代码。

You’ll need to edit the post.rb file to add the other side of the association:你将会需要编辑post.rb文件来添加其他association 盟友。

class Post < ActiveRecord::Base

validates :name, :presence => true

validates :title, :presence => true,

:length => { :minimum => 5 }

 

has_many :comments

end

These two declarations enable a good bit of automatic behavior. For example, if you have an instance variable @post containing a post, you can retrieve all the comments belonging to that post as the array @post.comments.

For more information on Active Record associations, see the ActiveRecordAssociations guide.

7.3 Adding a Route for CommentsComments添加路由

As with the home controller, we will need to add a route so that Rails knows where we would like to navigate to see comments.作为home controller,我们将还需要添加一个路由让Rails知道我们导航到哪里可以看到评论。Open up the config/routes.rb file again, you will see an entry that was added automatically for posts near the top by the scaffold generator, resources :posts, edit it as follows:再次打开config/routes.rb文件,你将会看到scaffold创建器在顶部为posts自动添加的入口,resources :posts,把它改成如下:

resources :posts do

resources :comments

end

This creates comments as a nested resource within posts.这里把comments作为一个嵌套资源放在posts中。This is another part of capturing the hierarchical relationship that exists between posts and comments.这是在存在的postscomments的分层关系的表现。

For more information on routing, see the RailsRoutingfromtheOutsideIn guide.关于routing的更多的信息,查看RailsRoutingfromtheOutsideIn guide

7.4 Generating a Controller构造一个Controller

With the model in hand, you can turn your attention to creating a matching controller.model已经到手了,你可以把你的注意力放到创建一个匹配的controller上了。Again, there’s a generator for this:类似的,像这样构造:

$ rails generate controller Comments

This creates six files and one empty directory:这里新建了6个文件和一个空目录。

  • app/controllers/comments_controller.rb – The controller
  • app/helpers/comments_helper.rb – A view helper file
  • test/functional/comments_controller_test.rb – The functional tests for the controller
  • test/unit/helpers/comments_helper_test.rb – The unit tests for the helper
  • app/views/comments/ – Views of the controller are stored here
  • app/assets/stylesheets/comment.css.scss – Cascading style sheet for the controller
  • app/assets/javascripts/comment.js.coffee – CoffeeScript for the controller

Like with any blog, our readers will create their comments directly after reading the post, and once they have added their comment, will be sent back to the post show page to see their comment now listed.就像大多数blog,我们的读者将会直接发表他们的评论在他们阅读post的时候,并且一旦他们添加评论成功,将会回到postshow页面去查看他们刚刚列出的评论。Due to this, our CommentsController is there to provide a method to create comments and delete SPAM comments when they arrive.正因为这样(的考虑),我们的CommentsController如下,它提供一个方法来创建comments和删除垃圾评论。

<p><%= notice %></p>

 

<p>

<b>Name:</b>

<%= @post.name %>

</p>

 

<p>

<b>Title:</b>

<%= @post.title %>

</p>

 

<p>

<b>Content:</b>

<%= @post.content %>

</p>

 

<h2>Add a comment:</h2>

<%= form_for([@post, @post.comments.build]) do |f| %>

<div>

<%= f.label :commenter %><br />

<%= f.text_field :commenter %>

</div>

<div>

<%= f.label :body %><br />

<%= f.text_area :body %>

</div>

<div>

<%= f.submit %>

</div>

<% end %>

 

<%= link_to ‘Edit Post’, edit_post_path(@post) %> |

<%= link_to ‘Back to Posts’, posts_path %> |

This adds a form on the Post show page that creates a new comment, which will call the CommentsController create action, so let’s wire that up:这里添加一个formsPost show页面用来创建一个新的评论,它将会调用CommentsControllercreat action,因此让我们补充上下面的内容:

class CommentsController < ApplicationController

def create

@post = Post.find(params[:post_id])

@comment = @post.comments.create(params[:comment])

redirect_to post_path(@post)

end

end

You’ll see a bit more complexity here than you did in the controller for posts.这里你看到的会比你在controller中为posts做的要复杂点。That’s a side-effect of the nesting that you’ve set up; each request for a comment has to keep track#踪迹of the post to which the comment is attached, thus the initial find action to the Post model to get the post in question.那就是你刚刚你刚补充的副作用的根源;每个面向comment的请求都保持了它所依附的post的踪迹,因此这样初始化find action的时候匹配相应的post model(时)得到了答案。

In addition, the code takes advantage of some of the methods available for an association.此外,上面的代码带来的好处就是使得一些对于association的方法可用。We use the create method on @post.comments to create and save the comment.我们使用@post.comments中的create方法来新建和保存commentThis will automatically link the comment so that it belongs to that particular post.这里将会自动连接到link使得comment依附于指定的post

Once we have made the new comment, we send the user back to the original post using the post_path(@post) helper.一旦我们评论过后,我们使用post_path(@post)助手导引用户到先前的postAs we have already seen, this calls the show action of the PostsController which in turn renders the show.html.erb template. 正如我们已经看到的,这里调用PostsControllershow action它将反过来渲染show.html.erb 模板。This is where we want the comment to show, so let’s add that to the app/views/posts/show.html.erb.这里也是我们想让comment显示的地方,因此让我们添加(那些代码)到app/views/posts/show.html.erb

<p><%= notice %></p>

 

<p>

<b>Name:</b>

<%= @post.name %>

</p>

 

<p>

<b>Title:</b>

<%= @post.title %>

</p>

 

<p>

<b>Content:</b>

<%= @post.content %>

</p>

 

<h2>Comments</h2>

<% @post.comments.each do |comment| %>

<p>

<b>Commenter:</b>

<%= comment.commenter %>

</p>

 

<p>

<b>Comment:</b>

<%= comment.body %>

</p>

<% end %>

 

<h2>Add a comment:</h2>

<%= form_for([@post, @post.comments.build]) do |f| %>

<div>

<%= f.label :commenter %><br />

<%= f.text_field :commenter %>

</div>

<div>

<%= f.label :body %><br />

<%= f.text_area :body %>

</div>

<div>

<%= f.submit %>

</div>

<% end %>

 

<br />

 

<%= link_to ‘Edit Post’, edit_post_path(@post) %> |

<%= link_to ‘Back to Posts’, posts_path %> |

Now you can add posts and comments to your blog and have them show up in the right places.现在你可以添加postscomments到你的blog同时随后他们会在相应的地方显示出来。

8 Refactoring重构

Now that we have Posts and Comments working, if we take a look at the app/views/posts/show.html.erb template, it’s getting long and awkward. 现在我们已经有PostsComments开始工作了,如果我们注意一下app/views/posts/show.html.erb 模板,发现它变得太长而且很别扭。We can use partials to clean this up.我们可以使用partials来整理它。

8.1 Rendering Partial(局部)Collections

First we will make a comment partial to extract showing all the comments for the post. 首先我们会创建一个comment partial来专门显示post的所有的commentsCreate the file app/views/comments/comment.html.erb and put the following into it:创建app/views/comments/comment.html.erb文件并输入下面的代码:

<p>

<b>Commenter:</b>

<%= comment.commenter %>

</p>

 

<p>

<b>Comment:</b>

<%= comment.body %>

</p>

Then in the app/views/posts/show.html.erb you can change it to look like the following:然后在app/views/posts/show.html.erb你可以相应的这样更改:

<p class=“notice”><%= notice %></p>

 

<p>

<b>Name:</b>

<%= @post.name %>

</p>

 

<p>

<b>Title:</b>

<%= @post.title %>

</p>

 

<p>

<b>Content:</b>

<%= @post.content %>

</p>

 

<h2>Comments</h2>

<%= render @post.comments %>

 

<h2>Add a comment:</h2>

<%= form_for([@post, @post.comments.build]) do |f| %>

<div class=“field”>

<%= f.label :commenter %><br />

<%= f.text_field :commenter %>

</div>

<div class=“field”>

<%= f.label :body %><br />

<%= f.text_area :body %>

</div>

<div class=“actions”>

<%= f.submit %>

</div>

<% end %>

 

<br />

 

<%= link_to ‘Edit Post’, edit_post_path(@post) %> |

<%= link_to ‘Back to Posts’, posts_path %> |

This will now render the partial in app/views/comments/comment.html.erb once for each comment that is in the @post.comments collection.这里会对@post.comments的每一个comment渲染app/views/comments/comment.html.erb模板。As the render method iterates over the @post.comments collection, it assigns each comment to a local variable named the same as the partial, in this case comment which is then available in the partial for us to show.当渲染方法迭代@post.comments收集器的时候,它声明每个comment为本地变量命名和partial相同(这里为comment),通过这样在partial中的comment就可以显示给我们的用户了。

8.2 Rendering a Partial Form

Lets also move that new comment section out to its own partial.同样让我们移动new comment部分到它自己的地方吧。Again, you create a file app/views/comments/form.html.erb and in it you put:类似的,创建一个文件app/views/comments/form.html.erb并且在里面放入下面代码:

<%= form_for([@post, @post.comments.build]) do |f| %>

<div class=“field”>

<%= f.label :commenter %><br />

<%= f.text_field :commenter %>

</div>

<div class=“field”>

<%= f.label :body %><br />

<%= f.text_area :body %>

</div>

<div class=“actions”>

<%= f.submit %>

</div>

<% end %>

Then you make the app/views/posts/show.html.erb look like the following:接着你这样修改app/views/posts/show.html.erb文件:

<p class=“notice”><%= notice %></p>

 

<p>

<b>Name:</b>

<%= @post.name %>

</p>

 

<p>

<b>Title:</b>

<%= @post.title %>

</p>

 

<p>

<b>Content:</b>

<%= @post.content %>

</p>

 

<h2>Comments</h2>

<%= render @post.comments %>

 

<h2>Add a comment:</h2>

<%= render “comments/form” %>

 

<br />

 

<%= link_to ‘Edit Post’, edit_post_path(@post) %> |

<%= link_to ‘Back to Posts’, posts_path %> |

The second render just defines the partial template we want to render, comments/form, Rails is smart enough to spot the forward slash in that string and realize that you want to render the form.html.erb file in the app/views/comments directory.第二个render仅仅定义了一个我们想渲染的partial template comments/formRails可以智能的识别字符串代表的含义,并且知道你是想render form.html.erb模板。

The @post object is available to any partials rendered in the view because we defined it as an instance variable.@post可以在任何的视图中partials rendered,因为我们把它定义成的实例变量。

9 Deleting Comments

Another important feature on a blog is being able to delete SPAM comments.另一个重要的功能就是可以删除垃圾评论。To do this, we need to implement a link of some sort in the view and a DELETE action in the CommentsController.要达到这样的效果,我们需要在view中实现某种链接和在CommentsController中的DELETE动作。

So first, lets add the delete link in the app/views/comments/comment.html.erb partial:首先,在app/views/comments/comment.html.erb partial中添加delete link

<p>

<b>Commenter:</b>

<%= comment.commenter %>

</p>

 

<p>

<b>Comment:</b>

<%= comment.body %>

</p>

 

<p>

<%= link_to ‘Destroy Comment’, [comment.post, comment],

:confirm => ‘Are you sure?’,

:method => :delete %>

</p>

Clicking this new “Destroy Comment” link will fire off a DELETE /posts/:id/comments/:id to our CommentsController, which can then use this to find the comment we want to delete, so let’s add a destroy action to our controller:点击Destroy Comment”link将会发送DELETE /posts/:id/comments/:id到我们的CommentsControllerCommentsController将会利用刚刚收到的(消息)找到我们想删除哪条评论,因此让我们接着添加一个destroy action到我们的controller

class CommentsController < ApplicationController

 

def create

@post = Post.find(params[:post_id])

@comment = @post.comments.create(params[:comment])

redirect_to post_path(@post)

end

 

def destroy

@post = Post.find(params[:post_id])

@comment = @post.comments.find(params[:id])

@comment.destroy

redirect_to post_path(@post)

end

 

end

The destroy action will find the post we are looking at, locate the comment within the @post.comments collection, and then remove it from the database and send us back to the show action for the post.destroy action将会找到那个我们正在阅读的post,并且定位comment@post.comments收集器,然后从数据库remove它,最后传回到postshow action

9.1 Deleting Associated Objects删除关联对象

If you delete a post then its associated comments will also need to be deleted. 如果你删除一个了post,那么与之相关联的comments也需要被删除。Otherwise they would simply occupy space in the database.否则他们将会只是在数据库中占用空间(别无它用)。Rails allows you to use the dependent option of an association to achieve this. Modify the Post model, app/models/post.rb, as follows:Rails允许你通过关系的依赖选项完成(上述功能)。修改Post model

class Post < ActiveRecord::Base

validates :name, :presence => true

validates :title, :presence => true,

:length => { :minimum => 5 }

has_many :comments, :dependent => :destroy

end

10 Security

If you were to publish your blog online, anybody would be able to add, edit and delete posts or delete comments.如果你就这样publish你的blog在互联网,任何人都可以添加,编辑和删除post或者删除comments

Rails provides a very simple HTTP authentication system that will work nicely in this situation.Rails提供了一个非常简单的HTTP认证系统在这样的情况下会非常适合。

In the PostsController we need to have a way to block access to the various actions if the person is not authenticated, here we can use the Rails http_basic_authenticate_with method, allowing access to the requested action if that method allows it.PostsController中我们需要一个方法来阻止没有通过认证的用户的操作,这里我们可以使用Railshttp_basic_authenticate_with这个方法,准许方法允许的请求的action

To use the authentication system, we specify it at the top of our PostsController, in this case, we want the user to be authenticated on every action, except for index and show, so we write that:要是用这个认证系统,我们需要在PostsController 的顶部在指定(即引用)它,这样我们希望用户在进行每个action的时候都是通过授权的,除了indexshow,因此我们这样写:

class PostsController < ApplicationController

 

http_basic_authenticate_with :name => “dhh”, :password => “secret”, :except => :index

 

# GET /posts

# GET /posts.json

def index

@posts = Post.all

We also only want to allow authenticated users to delete comments, so in the CommentsController we write:我们同样希望只有授权用户能够删除评论,因此在CommentsController这样写:

class CommentsController < ApplicationController

 

http_basic_authenticate_with :name => “dhh”, :password => “secret”, :only => :destroy

 

def create

@post = Post.find(params[:post_id])

Now if you try to create a new post, you will be greeted with a basic HTTP Authentication challenge.现在如果你尝试创建一个新的post,你将会迎来一个基于HTTP认证的挑战。

11 Building a Multi-Model Form构建一个多模型表单

Another feature of your average blog is the ability to tag posts. To implement this feature your application needs to interact with more than one model on a single form. Rails offers support for nested forms.另一个功能你的平衡的blog是能够给posts添加tag。要想在你的程序中实现这个功能需要在一个form中与超过一个model互动。Rails提供了嵌套forms

To demonstrate this, we will add support for giving each post multiple tags, right in the form where you create the post. First, create a new model to hold the tags:为了演示这个(功能),你将会在你创建postform中添加post的多tag支持。首先创建一个new model来存放tags

$ rails generate model tag name:string post:references

Again, run the migration to create the database table:再次运行migration来创建数据库表单:

$ rake db:migrate

Next, edit the post.rb file to create the other side of the association, and to tell Rails (via the accepts_nested_attributes_for macro) that you intend to edit tags via posts:接下来:编辑post.rb文件来创建来创建另一个成员,并且告诉Rails(通过the accepts_nested_attributes_for 宏)你打算通过posts form来编辑tags

class Post < ActiveRecord::Base

validates :name, :presence => true

validates :title, :presence => true

validates :content,:presence => true,

:length => { :minimum => 5 }

 

has_many :comments, :dependent => :destroy

has_many :tags

 

accepts_nested_attributes_for :tags, :allow_destroy => :true,

:reject_if => proc { |attrs| attrs.all? { |k, v| v.blank? } }

end

The :allow_destroy option on the nested attribute declaration tells Rails to display aremovecheckbox on the view that youll build shortly.对于:allow_destroy嵌套属性的声明是告诉Rails显示一个remove复选框在视图中那样你可以快速创建(tags)。The :reject_if option prevents saving new tags that do not have any attributes filled in.对于:reject_if保证不保存没有任何内容的tags

We will modify views/posts/form.html.erb to render a partial to make a tag:我们将要修改views/posts/form.html.erbrenderform的)一部分来创建tag

<% @post.tags.build %>

<%= form_for(@post) do |post_form| %>

<% if @post.errors.any? %>

<div id=“errorExplanation”>

<h2><%= pluralize(@post.errors.count, “error”) %> prohibited this post from being saved:</h2>

<ul>

<% @post.errors.full_messages.each do |msg| %>

<li><%= msg %></li>

<% end %>

</ul>

</div>

<% end %>

 

<div class=“field”>

<%= post_form.label :name %><br />

<%= post_form.text_field :name %>

</div>

<div class=“field”>

<%= post_form.label :title %><br />

<%= post_form.text_field :title %>

</div>

<div class=“field”>

<%= post_form.label :content %><br />

<%= post_form.text_area :content %>

</div>

<h2>Tags</h2>

<%= render :partial => ‘tags/form’,

:locals => {:form => post_form} %>

<div class=“actions”>

<%= post_form.submit %>

</div>

<% end %>

Note that we have changed the f in form_for(@post) do |f| to post_form to make it easier to understand what is going on.注意:我们已经更改form_for(@post) do |f|form_for(@post) do |post_form|这样会更加容易明白是怎么回事。

This example shows another option of the render helper, being able to pass in local variables, in this case, we want the local variable form in the partial to refer to the post_form object.这个例子在render helper中使用另个方式(使用f),是为了说明我们希望的是在form中使用局部变量指向的post_form对象。

We also add a @post.tags.build at the top of this form. This is to make sure there is a new tag ready to have its name filled in by the user. If you do not build the new tag, then the form will not appear as there is no new Tag object ready to create.我们还在form的顶部添加@post.tags.build。这里是为了确保每个新的tag都被用户填上了name。如果你不创建新tagform将不会显示它。

Now create the folder app/views/tags and make a file in there called form.html.erb which contains the form for the tag:现在创建一个app/views/tags文件夹并且在里面新建一个form.html.erb包含以下内容:

<%= form.fields_for :tags do |tag_form| %>

<div class=“field”>

<%= tag_form.label :name, ‘Tag:’ %>

<%= tag_form.text_field :name %>

</div>

<% unless tag_form.object.nil? || tag_form.object.new_record? %>

<div class=“field”>

<%= tag_form.label :_destroy, ‘Remove:’ %>

<%= tag_form.check_box :_destroy %>

</div>

<% end %>

<% end %>

Finally, we will edit the app/views/posts/show.html.erb template to show our tags.最后编辑app/views/posts/show.html.erb模板显示我们的tags

<p class=“notice”><%= notice %></p>

 

<p>

<b>Name:</b>

<%= @post.name %>

</p>

 

<p>

<b>Title:</b>

<%= @post.title %>

</p>

 

<p>

<b>Content:</b>

<%= @post.content %>

</p>

 

<p>

<b>Tags:</b>

<%= @post.tags.map { |t| t.name }.join(“, ”) %>

</p>

 

<h2>Comments</h2>

<%= render @post.comments %>

 

<h2>Add a comment:</h2>

<%= render “comments/form” %>

 

 

<%= link_to ‘Edit Post’, edit_post_path(@post) %> |

<%= link_to ‘Back to Posts’, posts_path %> |

With these changes in place, youll find that you can edit a post and its tags directly on the same view.通过这写修改,你会发现你可以直接在post form中编辑tags

However, that method call @post.tags.map { |t| t.name }.join(“, ”) is awkward, we could handle this by making a helper method.另外,@post.tags.map { |t| t.name }.join(“, ”)这个方法很别扭,我们可以通过编写一个helper method

###上面都还只能一次创建一个tagpost form

12 View Helpers

View Helpers live in app/helpers and provide small snippets of reusable code for views.View Helpers放置在app/helpers,它提供了可重用的小代码片段给viewIn our case, we want a method that strings a bunch of objects together using their name attribute and joining them with a comma.在本例,我们想要一个方法把(tag)放在一起(一个字符串中),并且使用逗号分割。As this is for the Post show template, we put it in the PostsHelper.要想这样在Post show模板,我们在PostHelper中写入:

Now you can edit the view in app/views/posts/show.html.erb to look like this:现在你可以在app/views/posts/show.html.erb中更改:

<p class=“notice”><%= notice %></p>

 

<p>

<b>Name:</b>

<%= @post.name %>

</p>

 

<p>

<b>Title:</b>

<%= @post.title %>

</p>

 

<p>

<b>Content:</b>

<%= @post.content %>

</p>

 

<p>

<b>Tags:</b>

<%= join_tags(@post) %>

</p>

 

<h2>Comments</h2>

<%= render @post.comments %>

 

<h2>Add a comment:</h2>

<%= render “comments/form” %>

 

 

<%= link_to ‘Edit Post’, edit_post_path(@post) %> |

<%= link_to ‘Back to Posts’, posts_path %> |

13 What’s Next?接下来做什么呢?

Now that you’ve seen your first Rails application, you should feel free to update it and experiment on your own. But you don’t have to do everything without help.现在你已经看到了你的第一个Rails应用程序,你应该可以很轻松的继续更新它或者试验一下你的想法。As you need assistance getting up and running with Rails, feel free to consult these support resources:当你在更新和运行Rails的时候需要援助,咨询下面推荐的资源会让你感到轻松:

Rails also comes with built-in help that you can generate using the rake command-line utility:Rails同样也带有内置的帮助你可以使用rake命令实用工具在你的应用程序中创建帮助文档:

  • Running rake doc:guides will put a full copy of the Rails Guides in the doc/guides folder of your application. Open doc/guides/index.html in your web browser to explore the Guides. 运行rake doc:guides将会输出所有Rails Guides的文档到你的应用程序中的doc/guides中。在你的浏览器中打开/guides/index.html浏览Guides
  • Running rake doc:rails will put a full copy of the API documentation for Rails in the doc/api folder of your application. Open doc/api/index.html in your web browser to explore the API documentation. 运行 rake doc:rails将会输出所有Rails API 的文档到你的应用程序中的doc/api中。

因为redcloth的问题文档支持有点故障,虽然找到了一种解决方法但是不够完美以待官方或者来人修复。

14 Configuration Gotchas配置陷阱

The easiest way to work with Rails is to store all external data as UTF-8.Rails使用Rails最简单的工作方式是存储所有的外部数据为UTF-8编码。If you don’t, Ruby libraries and Rails will often be able to convert your native data into UTF-8, but this doesn’t always work reliably, so you’re better off ensuring that all external data is UTF-8.如果不那样做,Ruby librariesRails通才会转换你的自然数据成UTF-8编码,但是这样不是很可靠,因此你最好保证所有的外部数据是UTF-8编码。

If you have made a mistake in this area, the most common symptom is a black diamond with a question mark inside appearing in the browser. 如果你在这里犯了错误,一般的症状就是在浏览器中出现钻石符号(可能是^)变成了问号。Another common symptom is characters like “ü” appearing instead of “ü”. 另一个普遍症状是ü”变成了ü”Rails takes a number of internal内部steps to mitigate减轻common causes of these problems that can be automatically detected检测and corrected. However, if you have external data that is not stored as UTF-8, it can occasionally偶尔result in these kinds of issues that cannot be automatically detected by Rails and corrected更正.

Two very common sources of data that are not UTF-8:两种非常普遍的不是UTF-8 编码的源数据:

  • Your text editor: Most text editors (such as Textmate), default to saving files as UTF-8. If your text editor does not, this can result in special characters that you enter in your templates (such as é) to appear as a diamond with a question mark inside in the browser. This also applies to your I18N translation files. Most editors that do not already default to UTF-8 (such as some versions of Dreamweaver) offer a way to change the default to UTF-8. Do so.
  • Your database. Rails defaults to converting data from your database into UTF-8 at the boundary. However, if your database is not using UTF-8 internally, it may not be able to store all characters that your users enter. For instance, if your database is using Latin-1 internally, and your user enters a Russian, Hebrew, or Japanese character, the data will be lost forever once it enters the database. If possible, use UTF-8 as the internal storage of your database.