锦囊妙计-后端篇

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 Toolboxawesome-rubyawesome-rails-gem 等介绍。

如何除错 Debug?

就三个步骤:

  • Reproduce: 有特定的步骤可以重现这个 bug
  • Isolate: 找出是哪一段 code 造成这个 bug
  • Fix: 修好他详细可以参考 The Art of Debugging 一文

Debug 方法

其他参考文章

如何查找 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 目录下即可,例如:

  1. # /lib/tasks/dev.rake
  2. namespace :dev do
  3. desc "Rebuild system"
  4. task :rebuild => ["db:drop", "db:setup", :fake]
  5. task :fake => :environment do
  6. puts "Create fake data for development"
  7. u = User.new( :login => "root", :password => "password", :email => "root@example.com", :name => "管理员")
  8. u.save!
  9. end
  10. 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 这篇文章。

分页

档案上传

  • Paperclip 是目前使用上最为方便的档案上传 plugin。
  • CarrierWave 写起来比较繁琐但设计比较严谨

范例 source code

paperclip 如何上传任意格式的档案?

假设字段叫做 attachment 的话,改成以下宣告:

  1. has_attached_file :attachment
  2. 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就可以拿到值了。

  1. development:
  2. secret_key_base: XXXXXX
  3. 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:

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)

many-to-many

ActiveReord: Tagging 标籤

可以自己写,也是多对多模型,搭配虚拟属性(Virtual Attribute)的设计来达到使用者可以输入任意的 tag nameTag model: name, taggings_countTagging: tag_id, {target_model}_id

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'

一些 API 资料来源

PDF

  • Prawn 可以产生 PDF,支援 Unicode。
  • PDFKit 则是另一个有趣的产生方式,透过 HTML/CSS 转 PDF。
  • Prince 是一套商用方案,将 HTML/CSS 转 PDF

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 等。

  1. require yaml
  2. ps2 = YAML.load_file(‘example.yaml’)
  3. ps2.each do |it|
  4. puts it.inspect
  5. end

JSON

Rails 内建就有 ActiveSupport JSON,用法如下:

  1. ActiveSupport::JSON.encode( [ {:a => 1 , :b => 2 } , "c", "d" ] )
  2. => "[{\"a\":1,\"b\":2},\"c\",\"d\"]"
  3. ActiveSupport::JSON.decode( "[{\"a\":1,\"b\":2},\"c\",\"d\"]" )
  4. => [{"a"=>1, "b"=>2}, "c", "d"]

yajl-ruby 则是一套底层用C,比较快很多的 JSON parser,建议可以让Rails底层改用这套函式库,请在Gemfile档案中加入

  1. gem 'yajl-ruby', :require => 'yajl'

XML

Rails 内建使用 Ruby 的 XML 函式库 Builder

Nokogiri 是一套基于 libxml2 的函式库,效能较佳。可参考 Getting Started with Nokogiri 一文介绍用法。

如果要替换 Rails 内建的 XML 函式库,请在Gemfile档案中加入

  1. gem 'nokogiri'

有些函式库为了执行效率,底层会改用 C 的函式库,适合于正式上线环境,缺点是需要编译,在一些特殊环境可能无法运作,例如最新版的 Nokogiri 就不支援 Windows 了。而纯 Ruby 实作的版本就没有这个问题。

排程工具

如果您有周期性的任务需要执行,除了可以透过Linuxcrontab设定去执行rake脚本。例如输入crontab -e加入:

  1. 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

小版号的升级,通常透过以下步骤即可完成:

  • 修改GemfileRails版本: gem 'rails', '3.1.1'
  • 执行bundle update
  • 执行rake rails:update 会尝试更新Rails自己产生的档案,例如config/boot.rb,请一一手动检查。

升级前,也请参阅官方公告的升级注意事项。

其他