Rails应用的助手:Rake背后的故事

开发 开发工具
作为一个Rails的开发者,你可能很熟悉使用Rake进行你的测试。这篇文章中,我们将讨论为什么要创建Rake,和他怎么样帮助我们的Rails应用,最好你可以写自己的Rake。

作为一个Rails的开发者,你可能很熟悉使用Rake进行你的测试,或者使用Rake db:migrate运行你的migrations,但是你真的知道Rake的背后故事吗?你意识到可以自己写一个Rake任务或者一个有用的lib吗?

51CTO推荐专题:Ruby On Rails开发教程

下面是我们使用Rake任务的例子:

1、给列表中的用户发送邮件

2、每晚数据的计算和报告

3、过期或重新生成缓存

4、备份数据和svn版本

5、运行数据处理脚本

一、历史回顾:Make

为了了解Rake的来历,我们先了解一下Rake的爷爷:Make。让我们回到那个代码块需要编译,解释性语言和iphone还没出现在地球上的时代。回到那时,我们下载的大型程序,还是一堆源代码和一个shell脚本。这个shell脚本包含了所有需要用来compile/link/build的代码。你需要运行“install_me.sh”这个脚本,每一行代码将被运行(编译每一行源文件),然后生成一个你能够运行的文件。

对于大多数人这样是不错的,但是对于程序开发人员却是一个不幸。每次你对源代码进行一个小的改动,并进行测试的时候,你需要回到shell脚本,并重新编译所有的源代码,显然对于大的程序“那是相当的”耗时的。

1977年贝尔实验室的Stuart Feldman创造了“make”。解决了编译时间过长的问题。Make用来编译程序,取得两方面的进步:

[[14049]] 
Stuart Feldman

(1)Make可以发现哪个文件在上一次编译后改动过,根据这点,再次运行Make时,仅编译改动过的文件。这个很大程序上减少了重新编译大型程序的时间。

(2)Make可以进行从属跟踪。你可以告诉编译器,源文件A的编译需要源文件B,源文件B的编译需要源文件C,所以Make在编译A时发现B没有编译,将会先编译B。

可以这样定义:Make是一个可执行程序。像ls或dir一样。让Make理解如何让编译一个项目,需要创建一个makefile文件,描述所有的源文件和依赖关系。makefiles有自己的语法,你不用去了解。

这些年Make出现了其他的变体,并且被其他的语言使用。事实上,Ruby用户在Rake出现前也在使用它。但是,Ruby并不需要编译,我们用它来干嘛?Ruby是一个解释性语言,我们不需要编译它的源代码,所以Ruby程序员为什么使用它呢?两个重要的原因:

(1)创建任务

在大型的应用中,你经常编写脚本,在命令行下运行一些任务。比如清除缓存,维护任务,或者迁移数据库。你可以写一个MakeFile来组织你的任务,而不是写十个不相干的脚本(或者一个复杂的)。这样你可以简单的运行:“make stupid”。

(2)从属任务跟踪

当你开始写一些维护任务的时候,可能发现有些任务的使用可能有重复。比如,“migrate”任务和“schema:dump”都需要链接数据库,这样我可以创建一个任务"connect_to_database",使“migrate”和“schema:dump”都依赖于"connect_to_database",这样下次运行“migrate”时,"connect_to_database"会先于“migrate”运行。

#p#

二、如何得到Rake

几年前,Jim Weirich在一个java项目上使用了Make,他发现如果在他的Makefile中写一小段Ruby代码将会带来非常大的方便。所以他创建了Rake。

[[14050]] 
Jim Weirich

Jim为Rake创建了任务功能,附属关系跟踪,甚至创建了时间段判断(timestamp recognition),(在上一次编译的基础上仅编译改动的部分),当然,对于Ruby,我们并不需要编译。我很想知道Jim在代码里做了什么,你也想知道吧。Jim可能从来没想给这个代码写个文档,可能现在他也是被烦透了,写了一个。

三、Rake如何工作

开始我想给这个部分起名为"How to get wasted with Rake"。那么我想喝点酒,该怎么做呢?

1、去买酒

2、喝酒

3、喝醉

如果我要使用Rake完成这个任务,我会创建一个“Rakefile”文件:

  1. task :purchaseAlcohol do    
  2.     puts "Purchased Vodka"  
  3. end  
  4. task :mixDrink do    
  5.     puts "Mixed Fuzzy Navel"end  
  6. task :getSmashed do    
  7.     puts "Dood, everthing's blurry, can I halff noth'r drinnnk?"  
  8. end 

这样我可以在这个Rakefile的目录,分别运行这些任务:
 

  1. $ Rake purchaseAlcohol   
  2.   Purchased Vodka  
  3. $ Rake mixDrink   
  4.   Mixed Fuzzy Navel  
  5. $ Rake getSmashed   
  6.   Dood, everthing's blurry, can I halff noth'r drinnnk? 

酷!但是从顺序上看,我可以用任何的顺序运行这个任务。比如喝醉在买酒或者喝酒之前。当然这不符合人的习惯。

四、Rake的顺序

  1. task :purchaseAlcohol do    
  2.      puts "Purchased Vodka"  
  3. end  
  4. task :mixDrink => :purchaseAlcohol do    
  5.      puts "Mixed Fuzzy Navel"endtask :getSmashed => :mixDrink do    
  6.      puts "Dood, everthing's blurry, can I halff noth'r drinnnk?"  
  7. end 

这样,如果想喝酒,就得先去买,如果想喝醉,就得先喝酒。

  1. $ Rake purchaseAlcohol   
  2.   Purchased Vodka  
  3. $ Rake mixDrink           
  4.   Purchased Vodka Mixed Fuzzy Navel  
  5. $ Rake getSmashed   
  6.   Purchased Vodka Mixed Fuzzy Navel   
  7. Dood, everthing's blurry, can I halff noth'r drinnnk? 

看到了吧,我喝醉和,因为酒已经买了,也被我喝了。现在,你的欲望无法满足了,你想让你的朋友加入进来。就像一个团队的开发,如果你想加入一个新人,你得有合适的规划。你得有文档。那么问题来了。

#p#

五、如何给我的Rake添加文档

Rake添加文档非常的方便,使用“desc”就可以了:

  1. desc "This task will purchase your Vodka"  
  2. task :purchaseAlcohol do    
  3.      puts "Purchased Vodka"  
  4. end  
  5. desc "This task will mix a good cocktail"  
  6. task :mixDrink => :purchaseAlcohol do    
  7.      puts "Mixed Fuzzy Navel"  
  8. end  
  9. desc "This task will drink one too many"  
  10. task :getSmashed => :mixDrink do    
  11.      puts "Dood, everthing's blurry, can I halff noth'r drinnnk?"  
  12. end 

看到了吧,我的每个任务都添加了desc,这样我们可以输入"Rake -T"或者"Rake --tasks":

  1. $Rake --tasks   
  2. Rake getSmashed       # This task will drink one too many   
  3. Rake mixDrink         # This task will mix a good cocktail   
  4. Rake purchaseAlcohol  # This task will purchase your Vodka 

简单乎?呵呵

六、Rake的命名空间

当你开始酗酒,并且开始使用大量的Rake任务的时候,你需要一个好方法将他们分类,这时用到了命名空间,如果我在上面的例子使用了命名空间,那么:

  1. namespace :alcoholic do    
  2.    desc "This task will purchase your Vodka"    
  3.    task :purchaseAlcohol do     
  4.       puts "Purchased Vodka"   
  5.    end    
  6.    desc "This task will mix a good cocktail"    
  7.    task :mixDrink => :purchaseAlcohol do      
  8.        puts "Mixed Fuzzy Navel"    
  9.    end   
  10.    desc "This task will drink one too many"    
  11.    task :getSmashed => :mixDrink do      
  12.        puts "Dood, everthing's blurry, can I halff noth'r drinnnk?"    
  13.    end  
  14. end 

命名空间允许你将一些任务放到特定的分类中,在一个Rakefile中,你可以加入几个命名空间。运行Rake --tasks

  1. Rake alcoholic:getSmashed        # This task will drink one too many  
  2. Rake alcoholic:mixDrink          # This task will mix a good cocktail  
  3. Rake alcoholic:purchaseAlcohol  # This task will purchase your Vodka 

所以如果想运行这个任务,只要输入 Rake alcoholic:getSmashed:

七、如何写一个有用的Ruby任务

最近,我想用Ruby创建几个文件夹:

  1. desc "Create blank directories if they don't already exist"  
  2. task(:create_directories) do      
  3.     # The folders I need to create    
  4.     shared_folders = ["icons","images","groups"]      
  5.  
  6. for folder in shared_folders          
  7.  
  8.     # Check to see if it exists      
  9.     if File.exists?(folder)        
  10.        puts "#{folder} exists"      
  11.     else        
  12.        puts "#{folder} doesn't exist so we're creating"        
  13.        Dir.mkdir "#{folder}"      
  14.     end        
  15.   end  
  16. end 

当然,还可以在Rake中使用更多的 文件工具File Utils,或者加入其他的部分。

八、如何为我的Rails应用写一个Rake任务

一个Rails应用中,已经有了一些Rake任务,你可以在你的项目目录里运行:Rake --tasks。为了给你的Rails应用添加一个新的任务,你可以打开/lib/tasks目录(已经存在的),添加一个叫something.Rake的文件,这个任务会被自动的检索到,这些任务会被添加到Rake tasks列表中,你可以在根目录里运行他们,现在把我们上面的例子放到这个Rails应用中。

  1. utils.Rake  
  2. namespace :utils do    
  3.    desc "Create blank directories if they don't already exist"    
  4.    task(:create_directories) do       
  5.      # The folders I need to create      
  6.      shared_folders = ["icons","images","groups"]                
  7.  
  8.      for folder in shared_folders                
  9.            
  10.          # Check to see if it exists        
  11.          if File.exists?("#{Rails_ROOT}/public/#{folder}")          
  12.          puts "#{Rails_ROOT}/public/#{folder} exists"        
  13.          else          
  14.          puts "#{Rails_ROOT}/public/#{folder} doesn't exist so we're creating"          
  15.          Dir.mkdir "#{Rails_ROOT}/public/#{folder}"        
  16.          end               
  17.      end    
  18.    end  
  19. end 

注意上面的代码,我使用了#{Rails_ROOT} 来得到Rails应用的当前位置,现在运行“Rake --tasks”,你可以看到我们的任务已经添加到tasks列表中了。

  1. ...  
  2. Rake tmp:pids:clear              # Clears all files in tmp/pids  
  3. Rake tmp:sessions:clear          # Clears all files in tmp/sessions  
  4. Rake tmp:sockets:clear           # Clears all files in tmp/sockets  
  5. Rake utils:create_directories    # Create blank directories if they don't already exist  
  6. ... 

#p#

九、如何在任务中调用Rails的model

呵呵,这个正是我最多使用Rake的地方,写一个Rake任务,代替原来需要手工操作的地方,或者一些任务代替经常需要按照计划自动执行(使用 cronjobs)的事情。就像我开头说的那样我用Rake任务执行下面的擦作:

1、给列表中的用户发送邮件

2、每晚数据的计算和报告

3、过期或重新生成缓存

4、备份数据和svn版本

5、运行数据处理脚本

这个补充了原来的功能,而且相当简单。下面这个任务是检查用户的过期时间,对快过期的用户发送邮件。

  1. utils.Rake  
  2. namespace :utils do    
  3.    desc "Finds soon to expire subscriptions and emails users"   
  4.    task(:send_expire_soon_emails => :environment) do         
  5.          # Find users to email          
  6.          for user in User.members_soon_to_expire                  
  7.                   puts "Emailing #{user.name}"                  
  8.                   UserNotifier.deliver_expire_soon_notification(user)          
  9.          end    
  10.    end  
  11. end 

使用你的model只用一步,"=> :environment"。

  1. task(:send_expire_soon_emails => :environment) do
  2.  

如果在我的开发环境上运行这个任务,我只需要"Rake utils:send_expire_soon_emails",如果我想在产品环境下运行这个任务,我需要"Rake Rails_ENV=production utils:send_expire_soon_emails"。

原文作者:Jason Seifer

文章来源:http://jasonseifer.com/2010/04/06/rake-tutorial

【编辑推荐】

  1. 跑起来吧 Ruby on Rails开发初体验
  2. Ruby on Rails性能优化七剑
  3. Ruby on Rails应用技巧全解析
  4. 总结各种Ruby on Rails命令
  5. Ruby on Rails验证输入技术讲解
责任编辑:王晓东 来源: 博客
相关推荐

2013-07-23 09:02:48

Facebook Ho应用设计

2012-10-17 13:50:25

2011-09-26 14:28:28

水果忍者

2018-11-06 15:56:25

西门子工业网络智能制造

2014-04-14 10:06:22

.Net 开源

2017-01-15 11:01:56

2016-12-12 14:19:59

LLVMClangApple

2018-02-07 10:08:02

应用服务器网络数据库

2015-07-14 15:44:18

渠道华为

2011-12-14 11:14:29

IT外包变化岳雷

2016-12-12 14:24:25

苹果Aqua界面

2023-09-26 07:33:41

2017-10-30 08:20:16

王者荣耀腾讯云游戏

2017-11-27 11:02:46

高并发突发池系统架构王者荣耀

2013-02-20 10:05:21

吴刚捕鱼达人

2012-06-19 16:11:56

2021-09-05 18:26:42

2015-06-18 11:28:18

谷歌

2022-10-21 11:50:04

2011-12-20 22:30:10

《失控》业务驱动云计算
点赞
收藏

51CTO技术栈公众号