上次已经介绍Generator的生成以及工作过程,下面通过源码来详细说明这个过程,先从command/generate.rb文件开始看,
1、rails_generators文件夹下的scripts.rb中的Rails::Generator::Scripts::Base类,截取部分代码:
module Rails
module Generator
module Scripts
# Generator scripts handle command-line invocation. Each script
# responds to an invoke! class method which handles option parsing
# and generator invocation.
class Base
include Options
default_options :collision => :ask, :quiet => false
# Run the generator script. Takes an array of unparsed arguments
# and a hash of parsed arguments, takes the generator as an option
# or first remaining argument, and invokes the requested command.
def run(args = [], runtime_options = {})
begin
parse!(args.dup, runtime_options)
rescue OptionParser::InvalidOption => e
# Don't cry, script. Generators want what you think is invalid.
end
# Generator name is the only required option.
unless options[:generator]
usage if args.empty?
options[:generator] ||= args.shift
end
# Look up generator instance and invoke command on it.
Rails::Generator::Base.instance(options[:generator], args, options).command(options[:command]).invoke!
rescue => e
puts e
puts " #{e.backtrace.join("\n ")}\n" if options[:backtrace]
raise SystemExit
end
#此处省略其他代码
end
这个类里的run方法是Generator的入口,方法的前面部分是解析用户输入的参数。后面Rails::Generator::Base.instance(options[:generator],args,options).command(options[:command]).invoke!,这句话生成一个Commands的实例(它是Generator的委托),调用Commands类中的invoke!方法,invoke!方法执行了manifest.replay(self),manifeast方法在下面第二个类说明中介绍。
module Rails
module Generator
module Commands
# Here's a convenient way to get a handle on generator commands.
# Command.instance('destroy', my_generator) instantiates a Destroy
# delegate of my_generator ready to do your dirty work.
def self.instance(command, generator)
const_get(command.to_s.camelize).new(generator)
end
# Even more convenient access to commands. Include Commands in
# the generator Base class to get a nice #command instance method
# which returns a delegate for the requested command.
def self.included(base)
base.send(:define_method, :command) do |command|
Commands.instance(command, self)
end
end
# Generator commands delegate Rails::Generator::Base and implement
# a standard set of actions. Their behavior is defined by the way
# they respond to these actions: Create brings life; Destroy brings
# death; List passively observes.
#
# Commands are invoked by replaying (or rewinding) the generator's
# manifest of actions. See Rails::Generator::Manifest and
# Rails::Generator::Base#manifest method that generator subclasses
# are required to override.
# Commands allows generators to "plug in" invocation behavior, which
# corresponds to the GoF Strategy pattern.
class Base < DelegateClass(Rails::Generator::Base)
# Replay action manifest. RewindBase subclass rewinds manifest.
def invoke!
manifest.replay(self)
after_generate
end
#此处省略其他代码
2、rails_generators文件夹下的base.rb中的Rails::Generator::Base类是代码生成器的骨架,设置源、目的路径和logger等信息。它有一个子类,NamedBase,比如model,controller等的generator就是继承了这个类。
Base# manifest方法没有实现,需要继承它的generator去实现,这个方法记录了Generator所作的操作,可以看看controller_generator.rb中,就只有这一个方法,里面是创建目录和拷贝文件等操作。
class ControllerGenerator < Rails::Generator::NamedBase
def manifest
record do |m|
# Check for class naming collisions.
m.class_collisions "#{class_name}Controller", "#{class_name}ControllerTest", "#{class_name}Helper", "#{class_name}HelperTest"
# Controller, helper, views, and test directories.
m.directory File.join('app/controllers', class_path)
m.directory File.join('app/helpers', class_path)
m.directory File.join('app/views', class_path, file_name)
m.directory File.join('test/functional', class_path)
m.directory File.join('test/unit/helpers', class_path)
# Controller class, functional test, and helper class.
m.template 'controller.rb',
File.join('app/controllers',
class_path,
"#{file_name}_controller.rb")
m.template 'functional_test.rb',
File.join('test/functional',
class_path,
"#{file_name}_controller_test.rb")
m.template 'helper.rb',
File.join('app/helpers',
class_path,
"#{file_name}_helper.rb")
m.template 'helper_test.rb',
File.join('test/unit/helpers',
class_path,
"#{file_name}_helper_test.rb")
# View template for each action.
actions.each do |action|
path = File.join('app/views', class_path, file_name, "#{action}.html.erb")
m.template 'view.html.erb', path,
:assigns => { :action => action, :path => path }
end
end
end
end
3、rails_generators文件夹下的manifest.rb中的Rails::Generator::Manifest类用来捕获Generator传递的动作,并调用Generator中对应的方法。这里面最重要的方法是Manifest#replay,这个方法通过send_actions调用generator的对应的方法。
module Rails
module Generator
class Manifest
attr_reader :target
# Take a default action target. Yield self if block given.
def initialize(target = nil)
@target, @actions = target, []
yield self if block_given?
end
# Record an action.
def method_missing(action, *args, &block)
@actions << [action, args, block]
end
# Replay recorded actions.
# 真正执行generator传入的action
def replay(target = nil)
send_actions(target || @target, @actions)
end
# Rewind recorded actions.
def rewind(target = nil)
send_actions(target || @target, @actions.reverse)
end
# Erase recorded actions.
def erase
@actions = []
end
private
def send_actions(target, actions)
actions.each do |method, args, block|
target.send(method, *args, &block)
end
end
end
end
4、rails_generators文件夹下的spec.rb中的Rails::Generator::Spec类用来装配指定的Generator,包含了Generator的Source和Templates等信息。
module Rails
module Generator
# A spec knows where a generator was found and how to instantiate it.
# Metadata include the generator's name, its base path, and the source
# which yielded it (PathSource, GemPathSource, etc.)
class Spec
attr_reader :name, :path, :source
def initialize(name, path, source)
@name, @path, @source = name, path, source
end
# Look up the generator class. Require its class file, find the class
# in ObjectSpace, tag it with this spec, and return.
def klass
unless @klass
require class_file
@klass = lookup_class
@klass.spec = self
end
@klass
end
def class_file
"#{path}/#{name}_generator.rb"
end
def class_name
"#{name.camelize}Generator"
end
private
# Search for the first Class descending from Rails::Generator::Base
# whose name matches the requested class name.
def lookup_class
ObjectSpace.each_object(Class) do |obj|
return obj if obj.ancestors.include?(Rails::Generator::Base) and
obj.name.split('::').last == class_name
end
raise NameError, "Missing #{class_name} class in #{class_file}"
end
end
end
klass方法将spec对象的klass域指向对应的generator类。class_file方法是用来查找generator的文件,在生成controller这个例子中就是指controller_generator.rb文件。class_name方法是调用的generator的类名,其中比较重要的方法是lookup_class,这个方法查找ObjectSpace中所有的对象,找出其中是继承自Rails::Generator::Base,同时类的名称又是class_name的对象。
PS:ObjectSpace是一个模块,用来操作所有的对象,它可以销毁对象可以遍历所有的对象,ObjectSpace#each_object方法:若某对象的kind_of?与class_or_module一致时,就对其进行迭代操作. 若省略参数,则对所有对象进行迭代操作.若将class_or_module指定为Fixnum或Symbol的话, 则不会进行任何操作.最后返回迭代操作的次数.
上面介绍的类是在Generator生成模板代码过程中涉及到的主要类,抽取了其中的一些关键方法进行一些说明,真正这些类是如何具体完成模板文件生成的,大家可以具体看看所有的源码。
分享到:
- 2009-07-13 09:45
- 浏览 1592
- 评论(0)
- 论坛回复 / 浏览 (0 / 2078)
- 查看更多
相关推荐
五、源码学习步骤 1. 阅读项目 README 文件,了解基本用法和安装步骤。 2. 分析 models 文件,理解数据获取逻辑。 3. 研究 controllers 文件,查看如何触发导出操作以及如何调用模型和视图。 4. 查看 views 文件,...
Ruby on Rails,通常简称为Rails,是一个基于Ruby编程语言的开源Web应用框架,遵循MVC(Model-View-Controller)架构模式...同时,对于有经验的开发者,这个项目也可以作为一个起点,进行二次开发或学习新的技术实践。
这个“ruby on rails 教程源码”很可能是为了辅助学习者深入理解Rails的工作原理和最佳实践,通过实际操作来提升技能。 在Rails中,`sample_app-master`可能是一个示例应用程序的主目录,它包含了完整的项目结构。...
Rails是Ruby语言的一个著名Web应用框架,以敏捷开发和“约定优于配置”...通过对这些版本的源码进行分析和学习,开发者不仅可以提升对Rails框架的理解,还能在实际项目中运用这些知识,编写出更高效、更安全的Web应用。
标签 "源码" 暗示了这篇笔记可能会涉及到一些实际的代码示例,读者可以通过阅读源码来理解和学习Rails查询的细节。而 "工具" 可能指的是Rails中用于辅助查询的工具或gem,如ActiveRecord的 scopes 和 relations,...
8. **配置**:Rails应用的配置信息位于`config`目录下,如`application.rb`和`environment.rb`。源代码揭示了如何根据环境(开发、测试、生产)调整应用配置。 9. **中间件**:Rails使用中间件栈来处理HTTP请求,每...
标题 "rails 的安装" 涉及到的是Ruby on Rails框架的安装过程,这是一个用于构建Web应用程序的开源框架。Rails是基于Ruby编程语言,它强调DRY(Don't ...学习和实践是掌握Rails的关键,祝你在Rails开发旅程中一切顺利!
通过分析这个源码,你可以学习如何使用Rails创建控制器、模型和视图,如何定义路由,如何处理表单提交,如何使用ActiveRecord进行数据库操作,以及如何编写测试确保代码质量。此外,你还可以了解Rails的自动化工具,...
《InfluxDB与Rails集成深度解析——以influxdb-rails源码为例》 InfluxDB,作为一款专为时间序列数据设计的高性能数据库,被广泛应用于监控、物联网、大数据分析等领域。Rails,作为Ruby on Rails框架的核心部分,...
在“ruby on rails社区网站开发源码”中,我们可以学习到如何利用Rails构建一个互动性强、功能丰富的社区网站。以下是一些关键知识点: 1. **安装与环境设置**:首先,你需要安装Ruby和Rails。这通常涉及设置Ruby...
《深入浅出Rails(影印版)》内容简介:通过此书,你将学习:希望你的网络应用超越平庸进入Web 2.0时代?《深入浅出Rails》将使你的编程和生产力达到最大值。你将学习一切Rails scaffolding的基本原理,以创建自定义的...
综合以上信息,学习和掌握Rails需要理解其核心组件和设计理念,熟练使用相关工具,阅读源码以加深对框架运作的理解,并通过实践项目来巩固理论知识。Rails是一个强大且高效的Web开发框架,它简化了许多常见的开发...
Rails 3.1 和 Cucumber-Rails 1.2.0 是两个在Web开发领域非常重要的工具,尤其对于Ruby on Rails框架的测试和自动化流程。本文将深入探讨这两个组件,以及它们如何协同工作来增强软件开发的效率和质量。 首先,...
标题中的“Rails相关电子书汇总二”表明这是一个关于Ruby on Rails框架的电子书籍集合,可能是对初学者到进阶者都有帮助的资源。Rails是基于Ruby语言的Web应用程序开发框架,以其“DRY(Don't Repeat Yourself)”...
总的来说,RailsGuides中的"blog"程序是学习Rails基础的一个理想起点,涵盖了MVC架构、数据库操作、路由配置、视图渲染、控制器逻辑等多个关键概念。通过分析和实践这个项目,开发者能够快速掌握Ruby on Rails的核心...
在学习Ruby on Rails之前,首先需要理解Ruby的基本概念,如变量、数据类型、控制结构(如if语句、循环)、类与对象、方法定义、模块和块等。 2. **Rails框架** Rails框架提供了一套完整的工具集,用于快速构建Web...
本教程聚焦于 "Ruby on Rails Tutorial 3" 的源码,这个版本对应于 Ruby 2.2.1 和 Rails 4.2.6 的环境。在这个环境中,开发人员可以学习如何利用 Rails 的强大功能来开发实际的应用。 首先,Ruby 2.2.1 是一个稳定...