锦囊妙计-后端篇
When you choose a language, you’re choosing more than a set of technical trade-offs—you’re choosing a community. - Joshua Bloch
这两章介绍一些常见的Rails疑难杂症问题,以及常用的RubyGem套件。更多热门套件可以参考 The Ruby Toolbox、awesome-ruby 和 awesome-rails-gem 等介绍。
如何除错 Debug?
就三个步骤:
- Reproduce: 有特定的步骤可以重现这个 bug
- Isolate: 找出是哪一段 code 造成这个 bug
- Fix: 修好他详细可以参考 The Art of Debugging 一文
Debug 方法
- 在 template 中使用
<%= debug your_variable %>
- 使用 Rails.logger
- 使用 web console 发生例外的时候会出现,或是自己放 console 到程式里面
使用中断点 byebug (使用指令 continue 让中断继续)byebug 详细用法请参考 https://github.com/deivid-rodriguez/byebug/blob/master/GUIDE.md
进一步找 Rails 或其他 gem 的原始码了解
- 执行
bundle open the_gem_name
会用默认的编辑器打开,可在~/.bash_profile
里面设定export EDITOR="subl"
- 执行
https://github.com/brentd/xray-rails 打开 template 和 partial 的好工具
https://github.com/rails/spring 有时候重开还是没用没有加载到修改的设定,可以试试 bin/spring stop
其他参考文章
- https://speakerdeck.com/igaiga/rubyconftaiwan2015
- http://tenderlovemaking.com/2016/02/05/i-am-a-puts-debuggerer.html 一些不用 debugger 只用 puts 的除错技巧
- http://rails.ruby.tw/debugging_rails_applications.html
- http://blog.motion-express.com/debugging-rails-5-skills/
- http://www.rubyonrails365.com/7-must-have-gems-to-install-on-any-project
- http://www.jackkinsella.ie/2014/06/06/a-comprehensive-guide-to-debugging-rails.html
如何查找 Gem 的原始码?
除了 Gem 的文件,直接查找 Gem 的原始码有时候也是除错或看用法的最好方式:
- 执行
bundle open GEM_NAME
就会用默认的编辑器打开 gem 的原始码了。 - 如果你想看 Rails 的原始码,由于 Rails 拆分成不同的 gems,所以
bundle open rails
打开来不会看到所有的 Rails 原始码。建议读者可以直接git clone git@github.com:rails/rails.git
一份官方的原始法回去即可。
Rake
Rake 用来编写任务脚本,让我们在CLI中可以执行。它的好处在于提供良好的任务编写结构,并且很方便设定各个任务的相依性,例如执行任务C前,需要先执行任务A、B。在 Rails 之中就内建了许多 rake 指令,除了你已经使用过的 rake db:migrate 之外,你可以输入 rake -T 看到所有的 rake 指令。
而要在 Rails 环境中撰写 Rake,请将附档名为 .rake 的档案放在 lib/tasks 目录下即可,例如:
# /lib/tasks/dev.rake
namespace :dev do
desc "Rebuild system"
task :rebuild => ["db:drop", "db:setup", :fake]
task :fake => :environment do
puts "Create fake data for development"
u = User.new( :login => "root", :password => "password", :email => "root@example.com", :name => "管理员")
u.save!
end
end
透过执行 rake dev:rebuild,就会砍掉重建数据库,最后执行 rake dev:setup 建立一些假资料作为开发之用。
推荐 https://github.com/stympy/faker 这个 gem 可以产生各种假资料
其他常见的使用情境包括:1. 修正上线的资料,这样部署到Production后,可以用来执行 2. 建立开发用的假资料 3. 搭配排成工具使用,例如每天凌晨三点寄出通知信、每周一产生报表等等
更多介绍可以参考 http://jasonseifer.com/2010/04/06/rake-tutorial 这篇文章。
分页
- kaminari 目前比较流行用这个
- will_paginate 比较老的专案会用这个
档案上传
- Paperclip 是目前使用上最为方便的档案上传 plugin。
- CarrierWave 写起来比较繁琐但设计比较严谨
范例 source code
- https://github.com/ihower/rails-exercise-ac4/commit/3fca6237babd4f462924d9c12696a8f17f0ef32c
- https://github.com/ihower/rails-exercise-ac6/commit/7de1031f64807de69bf83f32e46ae0ddab4f9414
paperclip 如何上传任意格式的档案?
假设字段叫做 attachment 的话,改成以下宣告:
has_attached_file :attachment
do_not_validate_attachment_file_type :attachment
设定档处理
在整合第三方应用时,第三方的 API Key 和 token 等等,我们不希望 hard-code 在程式码里面,一来是因为我们可能不想把这些敏感的 keys commit 进去版本控制系统。二来是因为将来布署的时候,在 staging 和 production 不同环境下,这些 key 都会不一样,我们希望容易抽换。
因此,有几种作法和考量:
使用 YAML 格式作为设定档放到 config 目录下
fb_config = YAML.load(File.read("#{Rails.root}/config/facebook.yml"))[Rails.env]
或 fb_config = Rails.application.config_for(:facebook)
要注意
- YAML 会区分数字和字串,例如
01234
会看成1234
,如果要确保被看成字串,请加上引号,例如"01234"
- 读出来的 Hash 是用字串 key,不是 symbol key。例如是
fb_config["app_id"]
而不是fb_config[:app_id]
- 如果要通吃,可以用
symbolize_keys
这个方法,例如fb_config = fb_config = Rails.application.config_for(:facebook).symbolize_keys
。这样用字串或 symbol 都可以。
如果不要 commit,请加进 .gitignore
因为设定档的东西不同开发者可能不一样、而且可能包括敏感资料,因此如果不要 commit,就列在 .gitignore 里面。
并且依照开发者惯例,我们会产生一个 xxx.yml.example 档案当作设定范例给同事参考。例如: 放 AWS key 的 s3.yml.example
、放 email 设定的 email.yml.example
一些 commit 也无妨的情况
但是如果该设定档的内容机密层级低、专案是放在 private repository 的话(各位的练习作业和期末专案是放在 public repository),而且每个开发者的设定档都长得一样,那么 commit 出去也无妨,大家开发会比较方便。例如config/database.yml
地的数据库帐号密码,大家可以讲好都设成一样帐号root
、密码留空即可。
除了写在 YAML 档案中,这些 token 也可以写进config/secret.yml
,例如以下的设定,用Rails.application.secrets.batchbook_api_key
就可以拿到值了。
development:
secret_key_base: XXXXXX
batchbook_api_key: YYYYYYYY
环境变量作法
也有一些人不喜欢用 yaml 加载,而是用环境变量。请参考 The Rubyist’s Guide to Environment Variables 这篇文章的作法。
通常是对 Linux 环境比较熟悉的同学,比较喜欢用这种作法。Heroku 也有专文 The twelve-factor app 推崇这种作法。
ActiveReord: 加强搜寻
- Ransack可以很快的针对ActiveRecord做出排序和复杂的条件搜寻。不过 ransack 并不太考虑效能问题,特别是文字模糊搜寻,这部份网站长大后会改用全文搜寻引擎,例如 ElasticSearch
ActiveReord: 列表结构(自订排列顺序)
搭配 jQuery UI Sortable 就可以做出拖拉排序,可以参考 Sortable Lists这篇文章。
Model 设计:
UI 设计可用 jQuery UI Sortable:
- https://github.com/joliss/jquery-ui-rails
- http://api.jqueryui.com/sortable/#event-update
- http://railscasts.com/episodes/147-sortable-lists-revised
Example Code: https://github.com/ihower/rails-exercise-ac5/blob/master/app/views/events/index.html.erb
ActiveReord: Self-referential Relationships
1-to-many tree 树状结构 (parent and children)
- 树状结构 比较 awesome_nested_set、ancestry 和 closure_tree
- 应用范例: http://railscasts.com/episodes/162-tree-based-navigation?view=asciicast
many-to-many
- http://railscasts.com/episodes/163-self-referential-association
- Friendship 朋友关系范例: https://github.com/ihower/social-exercise-ac3/blob/master/app/models/user.rb
ActiveReord: Tagging 标籤
可以自己写,也是多对多模型,搭配虚拟属性(Virtual Attribute)的设计来达到使用者可以输入任意的 tag nameTag model: name, taggings_countTagging: tag_id, {target_model}_id
- Tagging 教学文 http://www.sitepoint.com/tagging-scratch-rails/
- acts-as-taggable-on
- http://easyactiverecord.com/blog/2014/10/21/tagging-your-content-using-the-acts-as-taggable-on-gem/
ActiveReord: Soft Deletion 和版本控制,编辑和删除后还可以留下纪录和还原,
- paper_trail 另开一个 versions table 完整纪录
- paranoia 加一个字段标记被删除。不建议用这种方式,因为会让数据库的 unique key 检查出问题。
- audited
ActiveReord: 有限状态机
适合用来设计比较复杂的 model 流程状态
ActiveReord: 资料表注解
会帮你在model code上面注解加上所有资料表的字段
根据ActiveRecord的关联自动产生漂亮的Entity-Relationship Diagrams
API 串接:处理 HTTP
Ruby 内建的 Net::HTTP 设计的不是很好用,因此大多数人改用其他 API 比较好用的函式库,例如:
范例 Source Code
安装 gem 'rest-client'
- 抓 Ubike 资料 https://github.com/ihower/rails-exercise-ac8/commit/2eed28e94fc60aa7b7cc11cce531321e86254b0d#diff-d36dd0926c5b89774b950af54144b8b2R3
- 抓 Facebook 个人资料(需透过 token) https://github.com/ihower/rails-exercise-ac8/commit/1c1746c04b3eb08864de1512e7f7abf542aae381
一些 API 资料来源
- Github API: https://api.github.com/users/ihower
- 政府开放资料 http://data.gov.tw/
- g0v 开放资料 http://data.g0v.tw/
- SheetHub http://sheethub.com
- 资策会 Social Event Radar http://api.ser.ideas.iii.org.tw/docs/#!/fb_fanpage_search
- 台北市政府开放资料 http://data.taipei/
- YouBike台北市公共自行车即时资讯: API 端点 URL 和 字段说明文件
- 台北市政府 食材登录平台 https://taipeicity.github.io/foodtracer/
- 台北市政府 交通即时资料 开放资料专区 https://taipeicity.github.io/traffic_realtime/
CSV
Ruby就有内建这个函式库了,只需要require "csv"
即可使用。
YAML
Rails 的数据库设定档 database.yml 是用一种叫 : YAML Ain’t Markup Language 的格式所撰写,档案打开来,看起来就像一般的 plain 设定档,非常容易修改。
YAML 的设计首要目标就是要让使用者容易看懂,可以和 script 语言搭配良好。用途有 资料序列化 data serialization、设定档 configuration settings、log files、Internet messaging、filtering 等。网站上已知有支援的 script 语言有 Python,Ruby,Java,PHP,Perl,Javascript 等。
require ‘yaml’
ps2 = YAML.load_file(‘example.yaml’)
ps2.each do |it|
puts it.inspect
end
JSON
Rails 内建就有 ActiveSupport JSON,用法如下:
ActiveSupport::JSON.encode( [ {:a => 1 , :b => 2 } , "c", "d" ] )
=> "[{\"a\":1,\"b\":2},\"c\",\"d\"]"
ActiveSupport::JSON.decode( "[{\"a\":1,\"b\":2},\"c\",\"d\"]" )
=> [{"a"=>1, "b"=>2}, "c", "d"]
yajl-ruby 则是一套底层用C,比较快很多的 JSON parser,建议可以让Rails底层改用这套函式库,请在Gemfile
档案中加入
gem 'yajl-ruby', :require => 'yajl'
XML
Rails 内建使用 Ruby 的 XML 函式库 Builder
Nokogiri 是一套基于 libxml2 的函式库,效能较佳。可参考 Getting Started with Nokogiri 一文介绍用法。
如果要替换 Rails 内建的 XML 函式库,请在Gemfile
档案中加入
gem 'nokogiri'
有些函式库为了执行效率,底层会改用 C 的函式库,适合于正式上线环境,缺点是需要编译,在一些特殊环境可能无法运作,例如最新版的 Nokogiri 就不支援 Windows 了。而纯 Ruby 实作的版本就没有这个问题。
排程工具
如果您有周期性的任务需要执行,除了可以透过Linux的crontab设定去执行rake脚本。例如输入crontab -e加入:
0 2 * * * cd /home/your_project/current/ && RAILS_ENV=production /usr/local/bin/rake cron:daily
就是每天凌晨两点执行rake cron:daily这个任务。
或是你可以安装whenever这个 gem,就可以用Ruby的语法来定义周期性的任务,可以很方便的设定服务器上的cron排程。
自动备份
可以搭配 whenever 就可以定期备份了
升级Rails
小版号的升级,通常透过以下步骤即可完成:
- 修改
Gemfile
的Rails版本:gem 'rails', '3.1.1'
- 执行
bundle update
- 执行
rake rails:update
会尝试更新Rails自己产生的档案,例如config/boot.rb,请一一手动检查。
升级前,也请参阅官方公告的升级注意事项。