今天写了个简单的generator,发现比想像的要简单许多,参照的这篇贴,顺便翻译下。
以basic_auth为例,首先创建如下目录结构:
basic_auth
|-- USAGE
|-- basic_auth_generator.rb
`-- templates
|-- INSTALL
|-- controllers
| `-- user_controller.rb
|-- lib
| `-- basic_auth.rb
|-- migrate
| `-- create_users.rb
|-- models
| |-- notifications.rb
| `-- user.rb
|-- test
| |-- fixtures
| | `-- users.yml
| |-- functional
| | `-- user_controller_test.rb
| `-- unit
| `-- user_test.rb
`-- views
|-- notifications
| `-- forgot_password.rhtml
`-- user
|-- change_password.rhtml
|-- forgot_password.rhtml
|-- hidden.rhtml
|-- login.rhtml
|-- signup.rhtml
`-- welcome.rhtml
可以看到generator主要由两部分组成:
- 一个Templates目录,保存generator的模板文件
- 一个以generator结尾的rb文件,它负责拷贝模板文件到用户的Rails应用中,并在必要时对模板进行修改
要能够通过generate/destroy脚本调用到你的generator,basic_auth_generator.rb必须对Rails::Generator::Base进行扩展,并实现manifest方法:
class BasicAuthGenerator < Rails::Generator::Base
def manifest
record do |m|
# Controller
m.file "lib/basic_auth.rb", "lib/basic_auth.rb"
m.file "controllers/user_controller.rb", “app/controllers/user_controller.rb"
# Models
m.file "models/user.rb", "app/models/user.rb"
m.file "models/notifications.rb", "app/models/notifications.rb"
# Tests
m.file "test/unit/user_test.rb", "test/unit/user_test.rb"
m.file "test/functional/user_controller_test.rb", "test/functional/user_controller_test.rb"
m.file "test/fixtures/users.yml", "test/fixtures/users.yml"
# Views.
m.directory "app/views/notifications"
m.directory "app/views/user"
m.file "views/user/login.rhtml", "app/views/user/login.rhtml"
m.file "views/user/signup.rhtml", "app/views/user/signup.rhtml"
m.file "views/user/change_password.rhtml", "app/views/user/change_password.rhtml"
m.file "views/user/forgot_password.rhtml", "app/views/user/forgot_password.rhtml"
m.file "views/user/hidden.rhtml", "app/views/user/hidden.rhtml"
m.file "views/user/hidden.rhtml", "app/views/user/welcome.rhtml"
m.file "views/notifications/forgot_password.rhtml", "app/views/notifications/forgot_password.rhtml"
m.migration_template "migrate/create_users.rb", "db/migrate"
m.readme "INSTALL"
end
end
end
这段代码很直观,基本不需要解释,不过如果你的generator除了增加文件,还需要用户手动修改文件,那么你可能想在generate脚本的最后给出用户一些提示信息,下面是Restful Auth的做法:
def manifest
recorded_session = record do |m|
......
end
action = nil
action = $0.split("/")[1]
case action
when "generate"
puts
puts ("-" * 70)
puts "Don't forget to:"
puts
puts " - add restful routes in config/routes.rb"
puts " map.resources :#{model_controller_file_name}"
puts " map.resource :#{controller_singular_name.singularize}"
puts
puts ("-" * 70)
puts
when "destroy"
puts
puts ("-" * 70)
puts
puts "Thanks for using restful_authentication"
puts
puts ("-" * 70)
puts
else
puts
end
recorded_session
end
这将在用户调用generate/destroy脚本是分别给出一段提示。
如果你的generator需要根据用户参数动态生成代码及文件名称,那么你需要使用Rails::Generator::NamedBase来代替 Rails::Generator::Base,这会强制用户输入一个名称参数,你可以通过class_name(返回”Account”)和file_name(返回”account”)来得到用户希望创建的类名和文件名,并使用m.template来拷贝文件,m.template会在拷贝之前先使用erb来对文件进行解析,因此现在你的模板文件可能看起来是这个样子:
class <%= class_name %>Controller < ApplicationController
拷贝语句则是这个样子:
m.template "controllers/user_controller.rb", "app/controllers/#{file_name}_controller.rb"
如果以上这些还不能满足你的需求,那么建议你去现有的generator代码中学习,对于Rails来说,代码就是最好的文档。
