`
meladet
  • 浏览: 27249 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

Rails源码学习(二) 下

阅读更多
上次已经介绍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生成模板代码过程中涉及到的主要类,抽取了其中的一些关键方法进行一些说明,真正这些类是如何具体完成模板文件生成的,大家可以具体看看所有的源码。

分享到:
评论

相关推荐

    rails-exporter-源码.rar

    五、源码学习步骤 1. 阅读项目 README 文件,了解基本用法和安装步骤。 2. 分析 models 文件,理解数据获取逻辑。 3. 研究 controllers 文件,查看如何触发导出操作以及如何调用模型和视图。 4. 查看 views 文件,...

    Rails项目源代码

    Ruby on Rails,通常简称为Rails,是一个基于Ruby编程语言的开源Web应用框架,遵循MVC(Model-View-Controller)架构模式...同时,对于有经验的开发者,这个项目也可以作为一个起点,进行二次开发或学习新的技术实践。

    ruby on rails 教程源码

    这个“ruby on rails 教程源码”很可能是为了辅助学习者深入理解Rails的工作原理和最佳实践,通过实际操作来提升技能。 在Rails中,`sample_app-master`可能是一个示例应用程序的主目录,它包含了完整的项目结构。...

    Rails进行敏捷Web开发(所有版本的源码rails3.0-4.0)

    Rails是Ruby语言的一个著名Web应用框架,以敏捷开发和“约定优于配置”...通过对这些版本的源码进行分析和学习,开发者不仅可以提升对Rails框架的理解,还能在实际项目中运用这些知识,编写出更高效、更安全的Web应用。

    rails查询学习笔记

    标签 "源码" 暗示了这篇笔记可能会涉及到一些实际的代码示例,读者可以通过阅读源码来理解和学习Rails查询的细节。而 "工具" 可能指的是Rails中用于辅助查询的工具或gem,如ActiveRecord的 scopes 和 relations,...

    Ruby on Rails源代码

    8. **配置**:Rails应用的配置信息位于`config`目录下,如`application.rb`和`environment.rb`。源代码揭示了如何根据环境(开发、测试、生产)调整应用配置。 9. **中间件**:Rails使用中间件栈来处理HTTP请求,每...

    rails 的安装

    标题 "rails 的安装" 涉及到的是Ruby on Rails框架的安装过程,这是一个用于构建Web应用程序的开源框架。Rails是基于Ruby编程语言,它强调DRY(Don't ...学习和实践是掌握Rails的关键,祝你在Rails开发旅程中一切顺利!

    基于ruby on rails开发示例源码

    通过分析这个源码,你可以学习如何使用Rails创建控制器、模型和视图,如何定义路由,如何处理表单提交,如何使用ActiveRecord进行数据库操作,以及如何编写测试确保代码质量。此外,你还可以了解Rails的自动化工具,...

    influxdb-rails-源码.rar

    《InfluxDB与Rails集成深度解析——以influxdb-rails源码为例》 InfluxDB,作为一款专为时间序列数据设计的高性能数据库,被广泛应用于监控、物联网、大数据分析等领域。Rails,作为Ruby on Rails框架的核心部分,...

    ruby on rails社区网站开发源码

    在“ruby on rails社区网站开发源码”中,我们可以学习到如何利用Rails构建一个互动性强、功能丰富的社区网站。以下是一些关键知识点: 1. **安装与环境设置**:首先,你需要安装Ruby和Rails。这通常涉及设置Ruby...

    深入浅出Rails(中文版)

    《深入浅出Rails(影印版)》内容简介:通过此书,你将学习:希望你的网络应用超越平庸进入Web 2.0时代?《深入浅出Rails》将使你的编程和生产力达到最大值。你将学习一切Rails scaffolding的基本原理,以创建自定义的...

    Rails

    综合以上信息,学习和掌握Rails需要理解其核心组件和设计理念,熟练使用相关工具,阅读源码以加深对框架运作的理解,并通过实践项目来巩固理论知识。Rails是一个强大且高效的Web开发框架,它简化了许多常见的开发...

    关于rails 3.1 cucumber-rails 1.2.0

    Rails 3.1 和 Cucumber-Rails 1.2.0 是两个在Web开发领域非常重要的工具,尤其对于Ruby on Rails框架的测试和自动化流程。本文将深入探讨这两个组件,以及它们如何协同工作来增强软件开发的效率和质量。 首先,...

    Rails相关电子书汇总二

    标题中的“Rails相关电子书汇总二”表明这是一个关于Ruby on Rails框架的电子书籍集合,可能是对初学者到进阶者都有帮助的资源。Rails是基于Ruby语言的Web应用程序开发框架,以其“DRY(Don't Repeat Yourself)”...

    RailsGuides中那个blog程序源码

    总的来说,RailsGuides中的"blog"程序是学习Rails基础的一个理想起点,涵盖了MVC架构、数据库操作、路由配置、视图渲染、控制器逻辑等多个关键概念。通过分析和实践这个项目,开发者能够快速掌握Ruby on Rails的核心...

    ruby_on_rails 源代码上

    在学习Ruby on Rails之前,首先需要理解Ruby的基本概念,如变量、数据类型、控制结构(如if语句、循环)、类与对象、方法定义、模块和块等。 2. **Rails框架** Rails框架提供了一套完整的工具集,用于快速构建Web...

    Ruby on Rails Tutorial 3 源码

    本教程聚焦于 "Ruby on Rails Tutorial 3" 的源码,这个版本对应于 Ruby 2.2.1 和 Rails 4.2.6 的环境。在这个环境中,开发人员可以学习如何利用 Rails 的强大功能来开发实际的应用。 首先,Ruby 2.2.1 是一个稳定...

Global site tag (gtag.js) - Google Analytics