rails中的一些技巧

原创文章,转载请注明来源并保留原文链接

这篇文章可以说是10-things-you-didnt-know-rails-could-do10-most-underused-activerecord-relation-methods/的读书笔记,只列出我不熟悉的,所以如果想看全部内容可以参考上面的链接

1)pluck

原先我代码里确实充斥着published_book_titles = Book.published.map(&:title)

Good:

published_book_titles = Book.published.pluck(:title)

2)find_by (rails 4 only)

很多时候,我找单条记录一般是这样:

Book.where(:title => 'Three Day Road', :author => 'Joseph Boyden').first

更常用的是使用dynamic finder:

Book.find_by_title_and_author 'Three Day Road', 'Joseph Boyden'

如果使用find_by就可以这样:

Book.find_by(:title => 'Three Day Road', :author => 'Joseph Boyden')
# 或者你可以写成这样
Book.where(:title => 'Three Day Road').find_by :author => 'Joseph Boyden'

参考这边的讨论,可以看出find_by的目的是取代使用method_missing的dynamic finder,find_by的实质就是where().first,所以如果你不想等rails4,自己写一个也行。

3)none (rails 4 only)

其实不是返回空数组,而是返回一个NullRelation的实例,他的应用场景在于期望返回0条记录但是又可以使用chainable(查询链),官方的example:

@posts = current_user.visible_posts.where(:name => params[:name])
# => the visible_post method response has to be a chainable Relation

def visible_posts
  case role
  if 'Country Manager'
    Post.where(:country => country)
  if 'Reviewer'
    Post.published
  if 'Bad User'
    Post.none # => returning [] instead breaks the previous code
  end
end

并且类似于Post.none.published,不会向数据库发起请求。相关讨论pull request

4)scoping 和 unscoped

scoping这个用的不多

Comment.where(:post_id => 1).scoping do
  Comment.first # SELECT * FROM comments WHERE post_id = 1
end

unscoped用来取消default_scope的作用

class Post < ActiveRecord::Base
  def self.default_scope
    where :published => true
  end
end

Post.all          # Fires "SELECT * FROM posts WHERE published = true"
Post.unscoped.all # Fires "SELECT * FROM posts"

Post.unscoped {
  Post.limit(10) # Fires "SELECT * FROM posts LIMIT 10"
}

需要注意的是scope chain不受影响,下面两个等价:

Post.unscoped.published
Post.published

5)first_or_create 和 first_or_initialize

# Find the first user named Penélope or create a new one.
User.where(:first_name => 'Penélope').first_or_create

User.where(:first_name => 'Scarlett').first_or_create(:last_name => 'Johansson')

6)merge

有篇文章提到了merge这个方法,我特意去看了源码,还发现了几个有意思的地方。

关于default_scope
如果定义多个default_scope,他们会合并在一起,这里就用了merge:

class Article < ActiveRecord::Base
  default_scope { where(:published => true) }
  default_scope { where(:rating => 'G') }
end

还可以自定义default_scope方法:

class Article < ActiveRecord::Base
  def self.default_scope
    # Should return a scope, you can call 'super' here etc.
  end
end

merge在ActiveRecord::SpawnMethods模块中:

# Merges in the conditions from other,
# if other is an ActiveRecord::Relation.
Post.where(:published => true).joins(:comments).merge( Comment.where(:spam => false) )

# 如果传入的是数组则返回数组
recent_posts = Post.order('created_at DESC').first(5)
Post.where(:published => true).merge(recent_posts)

merge还是很有用的,类似的方法还有except和only

except:

# discards the order condition
Post.order('id asc').except(:order)

# discards the where condition but keeps the order
Post.where('id > 10').order('id asc').except(:where)

only:

# discards the order condition
Post.order('id asc').only(:where)

# uses the specified order
Post.order('id asc').only(:where, :order)

7)rake notes

原先我都是用一些TODO list软件,这里可以直接通过rake命令来提醒自己

class UserController < ApplicationController
  # TODO: BalaBala...
  # FIXME: ...
  # OPTIMIZE: ...
  # CUSTOM: ...
end

# 使用 use it!
rake notes
rake notes:todo
rake notes:fixme
rake notes:custom ANNOTATION=CUSTOM

8)rails r

有时候我们想进rails终端做简单的测试,很多时候都是rails c
如果不复杂可以直接用:

rails r 'p [Article, Comment, User].map(&:count)'
# => [0,0,0]

9)直接使用helper测试helper方法

原先我都是通过include xxx模块在console中调用helper方法的,原来还有这么简单的方式:

helper.time_ago_in_words 3.days.ago
# '3 days'

10)rails generate resource command

rails g resource user name:index email:uniq token:string{6} bio:text

rails g resource article user:references subject body:text

rails g resource comment user:belongs_to article:belongs_to body:text

11)inquiry,except

inquiry:

env = "production".inquiry
env.production?  # => true
env.development? # => false

except:

params = {controller: 'home', action: 'index', from: 'Google'}
params.except :controller, :action
# => {:from => 'Google'}

Leave a Reply

Your email address will not be published. Required fields are marked *