-
-
Save hinagiku/5428756 to your computer and use it in GitHub Desktop.
Revisions
-
nightire revised this gist
Mar 27, 2013 . 2 changed files with 0 additions and 4 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,5 +1,3 @@ ## ActiveRecord ### Finders This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,5 +1,3 @@ ## ActiveRecord ### Scopes -
nightire revised this gist
Mar 27, 2013 . 1 changed file with 1 addition and 1 deletion.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -11,7 +11,7 @@ _注:(r3 代表 Rails 3,r4 代表 Rails 4)_ match '/books/:id/purchase', to: 'books@purchase' ``` 用户可以很轻松地使用 <del>XSS Attack</del> CSRF Attack,比如使用这样一个链接: > CodeSchool 的 Rail 4 教程里写的是 XSS Attack,经查证和问询,证明这是 CodeSchool 的失误,应该会很快改正过来,再次先做一个修正,并向受到误导的朋友致歉 -
nightire revised this gist
Mar 27, 2013 . 1 changed file with 4 additions and 2 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -11,13 +11,15 @@ _注:(r3 代表 Rails 3,r4 代表 Rails 4)_ match '/books/:id/purchase', to: 'books@purchase' ``` 用户可以很轻松地使用 <de>XSS Attack</del> CSRF Attack,比如使用这样一个链接: > CodeSchool 的 Rail 4 教程里写的是 XSS Attack,经查证和问询,证明这是 CodeSchool 的失误,应该会很快改正过来,再次先做一个修正,并向受到误导的朋友致歉 ```html <a href="http://yoursite.com/books/4/purchase">Get It Free!</a> ``` 这会使用 GET 去请求这个资源,你绝对不想看到这种情况(你希望的是 POST),所以你要限制客户端可以访问此资源的方式。例如: ```ruby match '/books/:id?purchase', to: 'books@purchase', via: :post # :all 代表匹配所有的 HTTP methods -
nightire revised this gist
Mar 26, 2013 . 1 changed file with 51 additions and 0 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -251,6 +251,28 @@ Person.pluck(:id, :name) 现在将会返回包含两个字段的记录了,一个小小的但是很有用的改进。 --- ### Relation#unscope ```ruby Post.comments.except(:order) ``` 像上面这一句代码,你以为会排除 order 的排序,但却不尽然。因为如果 Comment 的 `default_scope` 是带有 order 的话,except 并无法改变 Post.comments 的查询结果。幸好 Rails 4 中多了一个新方法: ```ruby Post.comments.unscope(:order) == Post.comments.order ``` 这样会确保你想要的结果,而不必担心 `default_scope` 所造成的影响。另外,`unscope` 方法是支持多个参数的。 --- ### Partial inserts 当向数据库插入新的记录的时候,Rails 会对比缺省值,然后只把发生变化的字段放进 `INSERT` 语句里,剩下的部分由数据库自动填充。这一变化会使得增加记录效率更高,移除数据库字段也会更加安全。 ## ActiveModel ### ActiveModel::Model @@ -307,6 +329,35 @@ Easy and clear! ## Others ### Migration Helper #### #create_join_table Migration 文件里新添加了一个 Helper method, 专门用于为 HABTM 关系创建关联表: ```ruby create_join_table :categories, :products, :id => false do |f| f.integer :categories_id, :null => false f.integer :products_id, :null => false end ``` 现在主键会自己初始化为 nil,除非你用别的值覆盖它。 #### self.disable_ddl_transaction! 如果你选用的数据库支持 DDL Transaction,那么所有的数据库迁移会被包裹在一个事务中完成;然而某些 SQL 命令无法在事物内部成功执行,这会造成迁移的失败。在 Rails 4 中,你可以把这些造成失败的命令抽取出来放在一个单独的 migration 里,然后使用这个方法来禁止事务处理: ```ruby class ChangeSth < ActiveRecord::Migration self.disable_ddl_transaction! def change # some SQLs those can not execute in a transaction end end ``` ---- ### Schema Cache Dump 在产品环境中,Rails 应用在初始化的时候会把所有 model 的数据库模式(schema)载入至一个 schema cache(模式缓存)中。对那些拥有庞大数量的 models 的应用程序而言,Rails 4 提供了 schema cache dump(模式缓存转储)的新功能,用来加速应用程序的启动。你可以使用这个 rake task: -
nightire revised this gist
Mar 26, 2013 . No changes.There are no files selected for viewing
-
nightire revised this gist
Mar 26, 2013 . 1 changed file with 6 additions and 0 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -299,6 +299,12 @@ end Easy and clear! ## Association in Rails 4 相比 Rails 3,Rails 4 里的 Association 返回的不再是数组而是一个集合代理(CollectionProxy),这一变化是好是坏应该说莫衷一是,具体产生的影响由于演示起来篇幅过长,所以请移步[这篇博客](http://blog.olivierlacan.com/posts/associations-in-rails-4/) 总结起来就是输出到客户端的关系数据会有所变化,会影响到 JSON API,不过在适应了规则之后,前端工程师处理这些小变化应该是没什么问题的。 ## Others ### Schema Cache Dump -
nightire revised this gist
Mar 26, 2013 . 1 changed file with 13 additions and 1 deletion.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -239,6 +239,18 @@ Post.includes(:comments).where('comments.name' => 'foo' }) Post.includes(:comments).order('comments.name') ``` --- ### Relation#pluck `pluck` 方法现在可以接受多个参数了(每个参数代表数据库表中的一个字段): ```ruby Person.pluck(:id, :name) ``` 现在将会返回包含两个字段的记录了,一个小小的但是很有用的改进。 ## ActiveModel ### ActiveModel::Model @@ -309,4 +321,4 @@ config.active_record.use_schema_cache_dump = false ```bash $ RAILS_ENV=production bundle exec rake db:schema:cache:clear ``` -
nightire revised this gist
Mar 26, 2013 . 1 changed file with 31 additions and 3 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -169,7 +169,7 @@ Post.none.recent # 不会报错,这是对的查询 --- ### Relation#order `#order` 方法现在产生了一些新的变化,主要是针对生成的 SQL 语句,以下简明列举: @@ -207,7 +207,7 @@ User.order(:name, created_at: :desc) --- ### Relation#references 说到字符串形式的查询条件,在 Rails 4 中对于这样的代码: @@ -226,12 +226,16 @@ Post.includes(:comments).where("comments.name = 'foo'").references(:comments) ``` 然而对于 hash 形式的条件传递,就不需要特意声明了: ```ruby Post.includes(:comments).where(comments: { name: 'foo' }) # or Post.includes(:comments).where('comments.name' => 'foo' }) ``` 像下面这样没有条件的查询,尽管是字符串也无需声明 references ```ruby Post.includes(:comments).order('comments.name') ``` @@ -282,3 +286,27 @@ end ``` Easy and clear! ## Others ### Schema Cache Dump 在产品环境中,Rails 应用在初始化的时候会把所有 model 的数据库模式(schema)载入至一个 schema cache(模式缓存)中。对那些拥有庞大数量的 models 的应用程序而言,Rails 4 提供了 schema cache dump(模式缓存转储)的新功能,用来加速应用程序的启动。你可以使用这个 rake task: ```bash $ RAILS_ENV=production bundle exec rake db:schema:cache:dump ``` 这会生成一个 `db/schema_cache.dump` 文件,Rails 用它来加载 `SchemaCache` 实例的内部状态。 你可以选择关闭这个功能,编辑 `config/production.rb` 文件,添加这一行: ```ruby config.active_record.use_schema_cache_dump = false ``` 如果你要清除 schema cache,执行: ```bash $ RAILS_ENV=production bundle exec rake db:schema:cache:clear ``` -
nightire revised this gist
Mar 25, 2013 . 1 changed file with 1 addition and 1 deletion.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -229,7 +229,7 @@ Post.includes(:comments).where("comments.name = 'foo'").references(:comments) ```ruby Post.includes(:comments).where(comments: { name: 'foo' }) # or Post.includes(:comments).where('comments.name' => 'foo' }) # 像下面这样没有条件的查询,尽管是字符串也无需声明 references Post.includes(:comments).order('comments.name') -
nightire revised this gist
Mar 25, 2013 . 1 changed file with 1 addition and 1 deletion.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -134,7 +134,7 @@ else end ``` 但是这太丑了,不是么?你必须要检查查询数组里有没有东西,然后在明知没有的情况下再返回代表“没有”的空数组……多愚蠢啊~为什么 Rails 不能帮我们检查是否“没有”呢?在 Rails 4 里这变成了可能: ```ruby Class User < ActiveRecord::Base -
nightire revised this gist
Mar 25, 2013 . 1 changed file with 1 addition and 0 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -76,6 +76,7 @@ if author else Book.where('author IS NOT NULL') end ``` 现在,同样的需求在 Rails 4 里可以这样写: -
nightire revised this gist
Mar 25, 2013 . 1 changed file with 1 addition and 0 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -18,6 +18,7 @@ default_scope where(state: 'available') 这样定义 scope 就称之为 eager-evaluated,因为在进行查询之前并不知道具体要调用该查询的对象是谁。在 Rails 4 中,以上代码会抛出警告: > DEPRECATION WARNING: Using #scope without passing a callable object is deprecated > > DEPRECATION WARNING: Calling #default_scope without a block is deprecated 按照提示所说,你需要在定义 scope 的时候传递一个 proc 对象,所以修正的方法也很简单: -
nightire revised this gist
Mar 25, 2013 . 1 changed file with 282 additions and 0 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,282 @@ # 拥抱 Rails 4_3 ## ActiveRecord ### Scopes 顺着上一节的话题,我们继续讲讲 Scopes。在 Rails 4 当中,eager-evaluated scopes 不再推荐使用了,因为通常没搞清对象(obejct)的热心(eager)帮助往往会帮倒忙! > 附注:eager 这个词在这里不好翻译,其原意是有“热情”、“渴望”的含义,在这里代表“在预先不知道查询请求的对象时就先做查询”。这一点固然有积极的意义,但有时候也会带来意料不到的结果。请看下文: 举个例子: ```ruby scope :sold, where(state: 'sold') default_scope where(state: 'available') ``` 这样定义 scope 就称之为 eager-evaluated,因为在进行查询之前并不知道具体要调用该查询的对象是谁。在 Rails 4 中,以上代码会抛出警告: > DEPRECATION WARNING: Using #scope without passing a callable object is deprecated > DEPRECATION WARNING: Calling #default_scope without a block is deprecated 按照提示所说,你需要在定义 scope 的时候传递一个 proc 对象,所以修正的方法也很简单: ```ruby scope :sold, -> { where(state: 'sold') } default_scope -> { where(state: 'available') } ``` 为什么呢?看一个实际的例子就明白了: ```ruby scope :recent, where(published_at: 2.weeks.ago) ``` 这段代码的问题就在于 `2.weeks.ago` 的求值只会在这个 class 载入时发生一次,以后再调用的时候你还是会得到一模一样的值。 ```ruby scope :recent, -> { where(published_at: 2.weeks.ago) } scope :recent_red, recent.where(color: 'red') ``` 转变成 proc 对象后,当你再次调用它就会重新求值(上例第二行,recent_red 调用 recent,recent 会重新求值),于是此问题就解决了。 当然,你应该在所有的 scopes 里应用这一原则,因此上例最终应写成: ```ruby scope :recent, -> { where(published_at: 2.weeks.ago) } scope :recent_red, -> { recent.where(color: 'red') } ``` 这个变化可能不是那么新鲜,毕竟多数开发者在 Rails 3 的时候就是这么处理的,Rails 4 只是对未处理过的 scopes 报出警告而已,算是一个小小的变化。 --- ### Relation#not `#not` 是一个新方法,而且非常好用。我们先来看一段代码: ```ruby Book.where('author != ?', author) ``` 你知道这个查询有什么问题么?大部分情况下它工作良好,但如果 `author = nil` 的话,Rails 会产生如下 SQL 语句: ```sql SELECT "posts".* FROM "posts" WHERE (author != NULL) ``` 它能用,但是最后括号里的部分不符合 SQL 的语法规则,这会让许多“代码洁癖”患者感到寝食难安的!(玩笑)所以他们通常会写出如下无可奈何的临时解决方案: ```ruby if author Book.where('author != ?', author) else Book.where('author IS NOT NULL') end 现在,同样的需求在 Rails 4 里可以这样写: ```ruby Book.where.not(author: author) ``` 该查询生成的 SQL 语句非常标准: ```sql SELECT "posts".* FROM "posts" WHERE (author IS NOT NULL) ``` --- ### Relation#none `#none` 也是和 `#not` 一样棒的新方法,考察一下这段代码: ```ruby Class User < ActiveRecord::Base def visible_posts # 查询可见的帖子... case role # ...基于用户的角色 when 'Country Manager' Post.where(country: country) when 'Reviewer' Post.published when 'Bad User' ??? end end end ``` 那么,对于 Bad User 我们要求不返回任何帖子,你要怎么做?比较直觉性的做法就是返回一个空数组 `[]`,但是对于下面的代码来说: ```ruby @posts = current_user.visible_posts @posts.recent ``` 会报错: > NoMethodError: undefined method `recent' for []:Array 本着“头疼医头,脚疼医脚”的精神……你可以这么搞: ```ruby @posts = current_user.visible_posts if @posts.any? @posts.recent else [] end ``` 但是这太丑了,不是么?你必须要检查查询数组里有没有东西,然后在明智没有的情况下再返回代表“没有”的空数组……多愚蠢啊~为什么 Rails 不能帮我们检查是否“没有”呢?在 Rails 4 里这变成了可能: ```ruby Class User < ActiveRecord::Base def visible_posts case role when 'Country Manager' Post.where(country: country) when 'Reviewer' Post.published when 'Bad User' Post.none # 空即是空,无便是无…… end end end ``` 上例中的 `Post.none` 并不只是返回空数组,而是返回一个不去碰数据库的 `ActiveRecord::Relation`,你可以获得如下的查询: ```ruby @posts = current_user.visible_posts @posts.recent # 根据前文的条件,这个方法会产生三个可能的查询: # 1 Post.where(country: country).recent # 2 Post.published.recent # 3 Post.none.recent # 不会报错,这是对的查询 ``` --- # 排序 `#order` 方法现在产生了一些新的变化,主要是针对生成的 SQL 语句,以下简明列举: ```ruby class User < ActiveRecord::Base default_scope -> { order(:name) } end User.order("created_at DESC") ``` 以上代码在 3 和 4 里产生了有所区别的 SQL: ```sql /*in r3*/ SELECT * FROM users ORDER BY name asc, created_at desc /*in r4*/ SELECT * FROM users ORDER BY created_at desc, name asc ``` 另外,现在可以用 symbol 来代表排序的查询条件了: ```ruby # in r3 User.order('created_at DESC') User.order(:name, 'created_at DESC') # in r4 User.order(created_at: :desc) User.order(:name, created_at: :desc) ``` 这么做的好处还是为了增强一致性,并且利于使用 hash 传入查询条件。 --- # Relation#references 说到字符串形式的查询条件,在 Rails 4 中对于这样的代码: ```ruby Post.includes(:comments).where("comments.name = 'foo'") ``` 会抛出警告: > DEPRECATION WARNING: It looks like you are eager loading table(s) (one of: posts, comments) that are referenced in a string SQL snippet. (...) 所以你必须对字符串形式的查询显式声明其引用的表是哪一个,就像这样: ```ruby Post.includes(:comments).where("comments.name = 'foo'").references(:comments) ``` 然而对于 hash 形式的条件传递,就不需要特意声明了: ```ruby Post.includes(:comments).where(comments: { name: 'foo' }) # or Post.includes(:comments).where('comments.name': 'foo' }) # 像下面这样没有条件的查询,尽管是字符串也无需声明 references Post.includes(:comments).order('comments.name') ``` ## ActiveModel ### ActiveModel::Model Rails 3 中增加了 `ActiveModel` 使得我们可以创建和 `ActiveRecord` 一样的模型,拥有几乎全部功能却不需要和数据库关联,就像这样: ```ruby class SupportTicket include ActiveModel::Conversion include ActiveModel::Validations extend ActiveModel::Naming attr_accessor :title, :description validates_presence_of :title validates_presence_of :description end ``` 于是,你可以为其生成关系表单,做条件验证等等,非常方便。在 Rails 4 中,对 `ActiveModel` 做了小小的改进,现在你可以直接 include 它的“精简版”: ```ruby class SupportTicket include ActiveModel::Model attr_accessor :title, :description validates_presence_of :title validates_presence_of :description end ``` `ActiveModel::Model` 是一个“混编模组”: ```ruby # activemodel/lib/active_model/model.rb def self.included(base) base.class_eval do extend ActiveModel::Naming extend ActiveModel::Translation include ActiveModel::Validations include ActiveModel::Conversion end end ``` Easy and clear! -
nightire revised this gist
Mar 23, 2013 . 1 changed file with 1 addition and 1 deletion.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -135,7 +135,7 @@ SELECT "books".* FROM "books" WHERE "books"."author" = 'admin' ```ruby @book.update(post_params) # 会触发验证 @book.update_columns(post_params) # 构建 SQL 语句,直接执行于数据库层,不会触发验证 ``` 就这俩,不会搞错了吧?以前的方式也还能用,但是不排除会被废弃。既然 Rails 4 提供了更好用的方法,那就不要再犹豫了。 -
nightire revised this gist
Mar 23, 2013 . 1 changed file with 1 addition and 1 deletion.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -112,7 +112,7 @@ SELECT "books".* FROM "books" WHERE "books"."title" = 'Rails 4' AND "books"."aut ``` 注意,这里的 `after_create` 回调原本是在创建一条记录后立刻返回__所有作者是 Albert Yu 的记录__,但最终的结果却是__所有标题是 Rails 4 并且作者是 Albert Yu 的记录__。这是因为触发该回调函数的方法调用已经有了 `title: 'Rails 4'` 的作用域,于是产生了作用域叠加。 Rails 4 里推荐这样来做: -
nightire revised this gist
Mar 23, 2013 . 1 changed file with 1 addition and 1 deletion.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -8,7 +8,7 @@ _注:(r3 代表 Rails 3,r4 代表 Rails 4)_ ```ruby # routes.rb match '/books/:id/purchase', to: 'books@purchase' ``` 用户可以很轻松地使用 XSS Attack,比如使用这样一个链接: -
nightire revised this gist
Mar 23, 2013 . 1 changed file with 2 additions and 2 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -133,9 +133,9 @@ SELECT "books".* FROM "books" WHERE "books"."author" = 'admin' 是不是经常被 `#update_attributes` 和 `#update_attribute` 还有 `#update_column` 搞晕?好消息来了——Rails 4 重新整理了属性更新的方法,现在的方式简单明了: ```ruby @book.update(post_params) # 会触发验证 @book.update_columns(post_params) # 构建 SQL 语句,直接执行与数据库层,不会触发验证 ``` 就这俩,不会搞错了吧?以前的方式也还能用,但是不排除会被废弃。既然 Rails 4 提供了更好用的方法,那就不要再犹豫了。 -
nightire revised this gist
Mar 23, 2013 . 1 changed file with 8 additions and 8 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -23,21 +23,21 @@ Book.where(author: 'Albert Yu') 没人不爱它!而且还没完,同样的变化还有: ```ruby Book.find_all_by_title('Rails 4') # r3 way Book.find_last_by_author('Albert Yu') # r3 way Book.where(title: 'Rails 4') # r4 way Book.where(author: 'Albert Yu').last # r4 way ``` 动态的 `find_by` 也不例外: ```ruby Book.find_by_title('Rails 4') # 接收单个参数的用法在 r3 & r4 都可以 Book.find_by(title: 'Rails4') # 不过 r4 更偏爱这样写 Book.find_by_title('Rails 4', conditions: { author: 'Albert Yu' }) # 这就不好了,得改 Book.find_by(title: 'Rails4', author: 'Albert Yu') # Wow! 太棒了! ``` 统一使用 `find_by` 不仅有更好的一致性,而且更便于接收 hash 参数: -
nightire revised this gist
Mar 23, 2013 . 1 changed file with 173 additions and 0 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,173 @@ # 拥抱 Rails 4_2 ## ActiveRecord ### Finders ```ruby Book.find(:all, conditions: { author: 'Albert Yu' }) ``` 这种方法已经用了很久了吧?在 Rails 4 中,你会看到如下警告: > DEPRECATION WARNING: Calling #find(:all) is deprecated. Please call #all directly instead. You have also used finder options. These are also deprecated. Please build a scope instead of using finder options. 实际上,老式的 finders 已经被抽取成了 `activerecord-deprecated_finders` gem,你要还想用就得自己安装它。 在 Rails 4 中,推荐这样用: ```ruby Book.where(author: 'Albert Yu') ``` 没人不爱它!而且还没完,同样的变化还有: ```ruby Book.find_all_by_title('Rails 4') // r3 way Book.find_last_by_author('Albert Yu') // r3 way Book.where(title: 'Rails 4') // r4 way Book.where(author: 'Albert Yu').last // r4 way ``` 动态的 `find_by` 也不例外: ```ruby Book.find_by_title('Rails 4') // 接收单个参数的用法在 r3 & r4 都可以 Book.find_by(title: 'Rails4') // 不过 r4 更偏爱这样写 Book.find_by_title('Rails 4', conditions: { author: 'Albert Yu' }) // 这就不好了,得改 Book.find_by(title: 'Rails4', author: 'Albert Yu') // Wow! 太棒了! ``` 统一使用 `find_by` 不仅有更好的一致性,而且更便于接收 hash 参数: ```ruby book_param = { title: 'Rails 4', author: 'Albert Yu' } Book.find_by(book_param) ``` `find_by` 方法的内部实现其实很简单: ```ruby # activerecord/lib/active_record/relation/finder_methods.rb def find_by(*args) where(*args).take end ``` 这意味着这样用也没有问题: ```ruby Book.find_by("published_on < ?", 3.days.ago) ``` ### find\_or_* 这两种方法不再推荐使用了: ```ruby Book.find_or_initialize_by_title('Rails 4') Book.find_or_create_by_title('Rails 4') ``` 会抛出如下警告: > DEPRECATION WARNING: This dynamic method is deprecated. Please use e.g. Post.find_or_initialize_by(name: 'foo') instead. > DEPRECATION WARNING: This dynamic method is deprecated. Please use e.g. Post.find_or_create_by(name: 'foo') instead. 让我们从善如流: ```ruby Book.find_or_initialize_by(title: 'Rails 4') Book.find_or_create_by(title: 'Rails 4') ``` 还有一种容易让人迷惑的用法 ```ruby Book.where(title: 'Rails 4').first_or_create # 若找不到… Book.where(title: 'Rails 4').create ``` 这方法在 Rails 3 和 Rails 4 里都可以用,它先是查询是否有符合条件的记录,若没有就以该条件创建一个。听起来还不错,然而当存在这样的代码时,其表现就不是你想的那样了: ```ruby class Book < ActiveRecord::Base after_create :foo def foo books = books.where(author: 'Albert Yu') ... end end ``` 产生的 SQL 是: ```sql SELECT "books".* FROM "books" WHERE "books"."title" = 'Rails 4' AND "books"."author" = 'Albert Yu' ``` 注意,这里的 `after_create` 回调原本是在创建一条记录后立刻返回**所有作者是 Albert Yu 的记录**,但最终的结果却是**所有标题是 Rails 4 并且 作者是 Albert Yu的记录**。这是因为触发该回调函数的方法调用已经有了 `title: 'Rails 4'` 的作用域,于是产生了作用域叠加。 Rails 4 里推荐这样来做: ```ruby Book.find_or_create_by(title: 'Rails 4') # 若找不到… Book.create(title: 'Rails 4') ``` 这样就不会产生叠加副作用,真正的 SQL 语句如下: ```sql SELECT "books".* FROM "books" WHERE "books"."author" = 'admin' ``` ### #update & #update_column 是不是经常被 `#update_attributes` 和 `#update_attribute` 还有 `#update_column` 搞晕?好消息来了——Rails 4 重新整理了属性更新的方法,现在的方式简单明了: ```ruby @book.update(post_params) // 会触发验证 @book.update_columns(post_params) // 构建 SQL 语句,直接执行与数据库层,不会触发验证 ``` 就这俩,不会搞错了吧?以前的方式也还能用,但是不排除会被废弃。既然 Rails 4 提供了更好用的方法,那就不要再犹豫了。 ### Model.all 也不是所有的变化都那么显而易见的令人愉悦,一部分人大概会对接下来的变化感到不适应。以前普遍认为不要直接使用 `Model.all`,因为这会产生很严重的性能问题,开发者更倾向于先对 Model 进行 scope: ```ruby def index @books = Book.scoped if params[:recent] @books = @books.recent end end ``` 然而,Rails 4 会抛出如下警告: > DEPRECATION WARNING: Model.scoped is deprecated. Please use Model.all instead. WTF?`Model.all` 又回来了? 没错。不过你不用担心,Rails 4 里的 `Model.all` 不会立即执行对数据库的查询,而仅仅是返回一个 `ActiveRecord::Relation`,你可以继续进行链式调用: ```ruby def index @books = Book.all # 我不会碰数据库的哦,直到你告诉我下一个条件… if params[:recent] @books = @books.recent # 这时候我才会行动 end end ``` 当然,这并不是说不能用 scoped model 了,只不过是多了一层防范措施,以减少初学者不小心造成的性能问题。 -
nightire renamed this gist
Mar 23, 2013 . 1 changed file with 0 additions and 0 deletions.There are no files selected for viewing
File renamed without changes. -
nightire revised this gist
Mar 23, 2013 . 1 changed file with 9 additions and 9 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -6,7 +6,7 @@ Rails 3 提供了 `match` 方法供我们自定义 routes,然而我们要小 _注:(r3 代表 Rails 3,r4 代表 Rails 4)_ ```ruby # routes.rb match '/books/:id?purchase', to: 'books@purchase' ``` @@ -19,7 +19,7 @@ match '/books/:id?purchase', to: 'books@purchase' 你绝对不想看到这种情况,所以你要限制客户端可以访问此资源的方式。例如: ```ruby match '/books/:id?purchase', to: 'books@purchase', via: :post # :all 代表匹配所有的 HTTP methods # 或者 @@ -49,7 +49,7 @@ post '/books/:id?purchase', to: 'books@purchase' 同时还增加了一个 `#patch` 方法,可以在合适的时候使用: ```ruby test "update book with PATCH verb" do patch :update, id: @book, book: { title: @book.title } assert_redirected_to book_url(@book) @@ -62,7 +62,7 @@ end Concerns(关注点)是一种组织代码结构的方式,用来帮助开发者将复杂的逻辑和重复代码梳理清楚,我们在 Rails 4 中多次看到对于 Concerns 的设计和实现。先看一段老代码: ```ruby resources :messages do resources :comments resources :categories @@ -84,7 +84,7 @@ end 像这样的代码存在许多的重复,Rails 4 允许我们重构它: ```ruby concern :sociable do resources :comments resources :categories @@ -98,7 +98,7 @@ resources :articles, concerns: :sociable 可以通过传递参数来实现对个例的特化: ```ruby concern :sociable do |options| resources :comments, options resources :categories, options @@ -114,7 +114,7 @@ end 甚至我们可以抽取出来变成单独的类: ```ruby # app/concerns/sociable.rb class Sociable def self.call(mapper, options) @@ -154,7 +154,7 @@ Rails 3 无法永远摆脱这恼人的提示,因为它要同时兼容 1.8 和 线程安全的处理在 Rails 3 中已有,不过默认是关闭的: ```ruby # config/environments/production.rb MyApp::Application.configure do # Enable threaded mode @@ -164,7 +164,7 @@ end 这个方法在 Rails 4 中不推荐使用,新的线程安全机制在默认情况下就已经开启: ```ruby # config/environments/production.rb MyApp::Application.configure do config.cache_classes = true # 阻止类在请求中重新载入,并保证 Rack::Lock 不包含在中间件堆栈中 -
nightire created this gist
Mar 23, 2013 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,173 @@ ## Routes ### 小心地使用 Match(Rails 3 已实现) Rails 3 提供了 `match` 方法供我们自定义 routes,然而我们要小心使用它以避免“跨站脚本攻击”(XSS Attack)。比如像这样的 routes: _注:(r3 代表 Rails 3,r4 代表 Rails 4)_ ```ruby:r3 # routes.rb match '/books/:id?purchase', to: 'books@purchase' ``` 用户可以很轻松地使用 XSS Attack,比如使用这样一个链接: ```html <a href="http://yoursite.com/books/4/purchase">Get It Free!</a> ``` 你绝对不想看到这种情况,所以你要限制客户端可以访问此资源的方式。例如: ```ruby:r4 match '/books/:id?purchase', to: 'books@purchase', via: :post # :all 代表匹配所有的 HTTP methods # 或者 post '/books/:id?purchase', to: 'books@purchase' ``` 否则你就会收到如下错误提示: > You should not use the `match` method in your router without specifying an HTTP method. (RuntimeError) --- ### 新的 HTTP Verb:patch 过去我们使用 `put` 来完成对资源的更新请求,然而 `put` 本身是对整个资源(数据集合)进行更新,若要实现部分资源的更新(单个数据,或是几个产生变化的数据实体),`put` 就有点过重了,此时 `patch` 会更加合适。 `patch` 并不是什么新东西,此前就一直存在于 HTTP 1.1 协议规范之中,只不过这一次 Rails 4 把它正式的引入进来。在 Rails 4 中,`put` 和 `patch` 都指向 `controller#update`,在更新部分资源时(比如 @book)会使用 `patch`,生成类似下例中的页面元素: ```html <form action="/books/20" method="post"> <div style="margin:0;padding:0;display:inline"> <input name="utf8" type="hidden" value="✓" /> <input name="_method" type="hidden" value="patch" /> <!-- 关键就是这一行了 --> </div> </form> ``` 同时还增加了一个 `#patch` 方法,可以在合适的时候使用: ```ruby:r4 test "update book with PATCH verb" do patch :update, id: @book, book: { title: @book.title } assert_redirected_to book_url(@book) end ``` --- ### Concerns for Routing Concerns(关注点)是一种组织代码结构的方式,用来帮助开发者将复杂的逻辑和重复代码梳理清楚,我们在 Rails 4 中多次看到对于 Concerns 的设计和实现。先看一段老代码: ```ruby:r3 resources :messages do resources :comments resources :categories resources :tags end resources :posts do resources :comments resources :categories resources :tags end resources :articles do resources :comments resources :categories resources :tags end ``` 像这样的代码存在许多的重复,Rails 4 允许我们重构它: ```ruby:r4 concern :sociable do resources :comments resources :categories resources :tags end resources :messages, concerns: :sociable resources :posts, concerns: :sociable resources :articles, concerns: :sociable ``` 可以通过传递参数来实现对个例的特化: ```ruby:r4 concern :sociable do |options| resources :comments, options resources :categories, options resources :tags, options end resources :messages, concerns: :sociable resources :posts, concerns: :sociable resources :articles do concerns :sociable, only: :create end ``` 甚至我们可以抽取出来变成单独的类: ```ruby:r4 # app/concerns/sociable.rb class Sociable def self.call(mapper, options) mapper.resources :comments, options mapper.resources :categories, options mapper.resources :tags, options end end # config/routes.rb concern :sociable, Sociable resources :messages, concerns: :sociable resources :posts, concerns: :sociable resources :articles do concerns :sociable, only: :create end ``` ## 抛弃 Ruby 1.8.x 我们都听说 Rails 4 需要 Ruby 的版本不能小于 1.9.3,不过这一点所引起的变化通常都十分微妙,不容易让人注意到。 ### 聒噪的 nil 1.8.x 时代,`nil.id` 是合法的(一切都是对象!),但是不合理,经常惹人厌。于是 1.9.2 之后,逐渐使用 `object_id` 来代替,使用旧的 `id` 方法会抛出运行时错误: > RuntimeError: Called id for nil, which would mistakenly be 4 -- if you really wanted the id of nil, use object_id Rails 3 无法永远摆脱这恼人的提示,因为它要同时兼容 1.8 和 1.9,于是一旦碰上可能会出现的 `nil.id` 就会看到上面那个错误 在 Rails 4 的世界里,手起刀落,喀嚓~~~ 从此 nil 不再聒噪,世界终于清净了…… > NoMethodError: undefined method `id' for nil:NilClass ## 线程安全 线程安全的处理在 Rails 3 中已有,不过默认是关闭的: ```ruby:r3 # config/environments/production.rb MyApp::Application.configure do # Enable threaded mode # config.threadsafe! end ``` 这个方法在 Rails 4 中不推荐使用,新的线程安全机制在默认情况下就已经开启: ```ruby:r4 # config/environments/production.rb MyApp::Application.configure do config.cache_classes = true # 阻止类在请求中重新载入,并保证 Rack::Lock 不包含在中间件堆栈中 config.eager_load = true # 在新线程创建前加载全部代码 end ```