构建自己的MVC框架(Ruby语言实现)-- 第一章 从零到“它工作了!”

Posted 百万架构师之道

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了构建自己的MVC框架(Ruby语言实现)-- 第一章 从零到“它工作了!”相关的知识,希望对你有一定的参考价值。


1. 从零到“它工作了!”

既然我们已经安装好了,到了开始构建的时候了。像Rails一样,你的框架将是一个gem(Ruby 的库),gem就像其他语言的库,可以包含、引用。整本书中,我们的框架的名字都是“Rules” ,就像“Ruby on Rails”。

开垦“荒地”

首先创建一个新的、空的 gem

$ bundle gem rulers
      create  rulers/Gemfile
      create  rulers/Rakefile
      create  rulers/LICENSE.txt
      create  rulers/README.md
      create  rulers/.gitignore
      create  rulers/rulers.gemspec
      create  rulers/lib/rulers.rb
      create  rulers/lib/rulers/version.rb
Initializating git repo in src/rulers”

rulers是我们创建的gem(库),我们可以在自动生成的rulers.gemspec文件里定义它所需的其他依赖 。使用你喜欢的文本编辑器里打开这个文件。你可以给你的gem起个你喜欢的名字,gem的功能描述 等等。你可以像这样自定义多个部分:

# rulers.gemspec
gem.name    = "rulers"
gem.version = Rulers::VERSION
gem.authors = ["Singleton Ruby-Webster"]
gem.email       = ["webster@singleton-rw.org"]
gem.homepage    = ""
gem.summary     = %q{A Rack-based Web Framework}
gem.description = %q{A Rack-based Web Framework,
                     but with extra awesome.}

按照惯例,摘要(summary)和描述(description)差不多,就是更短一些。摘要通常只有一样,然而描述有时会长达4-5行。

记住一定要把这个文件里“FIXME”和“TODO”这样的字符替换掉,“gem build”不喜欢它们,而且它们对用户也不友好。

你需要在底部添加依赖库。它们可能和下面的内容差不多:

gem.add_development_dependency "rspec"
gem.add_runtime_dependency "rest-client"
gem.add_runtime_dependency "some_gem""1.3.0"
gem.add_runtime_dependency "other_gem""> 0.8.2”

每一行添加一个运行时依赖(在运行gem的时候需要)或者开发时需要的依赖(在开发或测试 的时候用到的gem)。目前,只需添加下面的一行:

gem.add_runtime_dependency "rack"

完整的rulers.gemspec文件如下:

# rulers.gemspec (此为文件名,不需要复制这行,下同)

# coding: utf-8
lib = File.expand_path('../lib', __FILE__)
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
require 'rulers/version'

Gem::Specification.new do |spec|
  spec.name          = "rulers"
  spec.version       = Rulers::VERSION
  spec.authors       = ["your_name"]
  spec.email         = ["your_name@example.com"]

  spec.summary       = '基于Rack的类Rails的框架'
  spec.description   = %q{基于Rack的简单Web框架,但是额外酷!}
  spec.homepage      = "http://rebuilding-rails.com"
  spec.license       = "MIT"

  # Prevent pushing this gem to RubyGems.org by setting 'allowed_push_host', or
  # delete this section to allow pushing this gem to any host.
  if spec.respond_to?(:metadata)
    spec.metadata['allowed_push_host'] = 'http://mygemserver.com'
  else
    raise "RubyGems 2.0 or newer is required to protect against public gem pushes."
  end

  spec.files         = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
  spec.bindir        = "exe"
  spec.executables   = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
  spec.require_paths = ["lib"]

  spec.add_development_dependency "bundler""~> 1.10"
  spec.add_development_dependency "rake""~> 10.0"
end

rack也是一个gem,是你的框架和Ruby应用程序服务器,例如Mongrel、Thin、Lighttpd、Passenger、WEBrick或Unicorn之间的接口。应用服务器是特殊类型的网页服务器,运行服务器程序,通常是用Ruby写的。在真实的生产环境,会在应用程序服务器前再运行像Apache或者nginx这样的网页服务器。但是在开发环境我只运行应用程序服务器,其他的都不运行。幸运的是,应用程序服务器作为网页服务器效果也还不错。

我们会在控制器那章覆盖更多Rack的细节,然后在中间件(Middleware)再次覆盖一部分。现在你只需要知道Ruby通过Rack把HTTP请求变成在你的服务器上运行的代码。

修改lib/rulers/version.rb文件,将‘0.1.0’改为‘0.1.1’。

让我们打包你的gem(译者注:Ruby是动态语言,不需要编译),然后安装它:

$ gem build reulers.gemspec
$ gem install rulers-0.0.1.gem

最终我们将从开发目录使用Bundler的技巧来使用你的gem。但是现在我们只是用简单的方法 来安装它。知道完成一个任务最简单的方法总是好的--当聪明的技巧不起作用的适合,你可 以回头使用它。

Hello World,或多或少

Rails就像你刚才打包的,但是它运行的是什么程序?我们将从一个很简单的app开始,你可以提交最爱的名言,用户可以给它们打分。Rails会为这个任务使用生成器(“rails new best_quotes"),但是我们将手动构建。

创建一个目录和一些子目录:

$ mkdir best_quotes
cd best_quotes
$ git init
Initialized empty Git repository in src/best_quotes/.git/
$ mkdir config
$ mkdir app”

你也想确定使用你的库。添加Gemfile:

# best_quotes/Gemfile
source 'https://ruby.taobao.org/'
gem 'rulers' #你创建的gem

然后运行“bundle install”来创建Gemfile.lock,确保所有的依赖都可以使用。

我们先构建一个小小的Rack程序。创建config.ru文件:

#best_quotes/config.ru
run proc {
  [200, {'Content-Type' => 'text/html'},
   ['Hello, World!']]
}

Rack的run意味着 对每个请求去调用这个对象。在这里,这个 proc 返回成功(200)和 Hello, world! 以及HTTP header来确保浏览器能正常显示HTML。

既然你现在已经有了个简单的可以显示Hello, world! 的 APP, 我们现在通过命令行来启动它:

$ rackup -p 3001

你应该能看到 “Hello, world!”。它来自于 config.ru 文件。

(无法启动rackup? 确定一下安装Ruby的时候是否PATH环境变量是否添加了gem的目录,也可以下载一下Ruby版本管理工具rvmrbenv 来帮你完成这些事情。)

使用Rack创建Rulers

在你的Rulers的目录,打开lib/rulers.rb。修改如下:

# rulers/lib/rulers.rb
requrie 'rulers/version'

module Rulers
  class Application
    def call(env)
      [200, {'Content-Type' => 'text/html'},
        ['Hello from Ruby on Rulers']]
    end
  end
end

我们重新打包、安装一下rulers

$ gem build rulers.gemspec
$ gem install rulers-0.0.1.gem

现在切换到你应用程序目录best_quotes:

现在你可以使用Rulers::Application类了。在config目录下新建文件config/application.rb,然后添加以下代码:

# best_quotes/config/application.rb
require 'rulers'

module BestQuotes
  class Application < Rulers::Application
  end
end

BestQuotes module使用了你的Rulers框架,当你使用它的时候会显示Hello from Ruby on Rulers。为了使用它,打开你的config.ru,然后修改如下:

require './config/application'

run BestQuotes::Application.new

现在当你在命令行输入:$ rackup -p 3001

然后在浏览器输入http://localhost:3001,你应该看到Hello from Ruby on Rulers!

你已经用你自己的框架创建了一个应用程序!

复习

在这章,你创建了一个可重复使用的Ruby库,也就是gem,然后你把你自己写gem包含进示例应用程序。你也建立了一个简单的Rack程序,你可以使用Rackup文件,即config.ru来创建。你已经学到了Rack的基本用法,还把这些东西连接起来让它们工作。

从这里开始,你将逐渐添加一些新东西进去。但是这章是唯一的一次从白板开始,从什么也 没有到有一点功能。

给自己敬个礼吧。

在Rails里

默认情况下,Rails包含不止一个,而是五个可以复用的gem。真正的Railsgem仅有很少的代码。相反,它分发给其他的gem来做具体实现。Rails本身只是把它们连接在一起。

Rails允许你自定义各种组件, 比如你可以使用不同的ORM,不同的测试库,不同的Ruby模板库或者不同的javascript库。所以下面的描述对于由程序员严重自定义的Rails APP来说不一定准确。不过对于大部分情况下的Rails APP来说一般都适用。

下面是一些基本的rails gem -- 没有其他依赖和库,只是rails本身基本的一部分内容。

  • ActiveSupport是一个功能扩展库,它里面的方法不是专为Rails设计的。你会看见 一些不是Rails的库里也使用了ActiveSupport。ActiveSuppot包含了一些像Rails怎么变换单 词的单复数,或怎么把驼峰(CamelCase)名称转换成蛇形(snack_case)名称。它也包括了 比Ruby标准库更加丰富的时间和日期支持。

  • ActiveModel主要处理model,或者其他语言所说的Entity。它并不是一个针对数据库的实现。

    • 例如,假如你想要某个模型的URL,ActiveRecord也可以帮你实现。它只是包装了许多不 同ActiveModel的实现来告诉Rails怎样使用他们。更宽泛的说,ActiveModel实现是ORM(见 ActiveRecord,下面提到),但是它也能让我们使用非关系型存储,像MongoDB,Redis, Memcached或者甚至只是本地机器存储。
  • ActiveRecord是一个对象-关系映射(ORM)。这意味着它在Ruby对象和SQL数据库表之间 相互映射。当你在Rails里从SQL数据库查询或者写入数据,你都是通过ActiveRecord。ActiveRecord也是ActiveModel的实例。ActiveRecord支持mysql和SQLite、也支持JDBC、 Oracle、PostgreSQL和许多其他的关系型数据库。

  • ActionPack负责路由 - 输入的URL到Rails里的控制器(Controller)和动作(Action) 之间的映射。它也用来创建Rails 中的 controller和action,然后引导request遍历controller,以及action,然后渲染视 图view。这里中的一些功能,ActionPack通过Rack来实现。而视图渲染则是引入其他的外部的gem来渲染template, 比如用Eruby渲染.erb模板、Haml渲染.haml模板。ActionPack也处理action - 或以view为中心的功能,如view缓存。

  • ActionMailer 则用来发送邮件,尤其是基于模板的email。它具有你期望Rails email能具有的 功能,有控制器、动作、和“视图” - 对于email来是说基于文本的模板,不是普通的网页模板。

当然,Rails里也不是啥都有。

一些你在这章里构建的功能是在App里,不是在Rulers里。

继续向前,创建新的Rails 3应用 - 输入 rails new test_app

假如你看看config/application.rb,你会看见Rails 建立了一个Rails Application对象,非常像Rulers Application对象。

现在该是打开config目录,看看Rails Application默认生成了些什么的时候了。

你看到一些现在觉得更合理的代码了吗?

练习

练习1. 重新加载Rulers

让我们给Rulers框架添加一点调试代码。

# rulers/lib/rulers.rb
module Rulers
  class Application
    def call(env)
     `echo debug > debug.txt`;
     [200, {'Content-Type' => 'text/html'},
       ['Hello from Ruby on Rulers!']]
    end
  end
end

当这个运行时,它应该在best_quotes文件夹下创建一个名为debug.txt的文件,也就是你运 行rackup的那个目录。

试着重启你的服务器,刷新你的浏览器页面“http://localhost:3001”。但是你找不到debug 文件!

试试重新打包gem,然后重新安装它

$ gem build rulers
$ gem install rulers-0.0.1.gem

刷新浏览器。你仍看不见它。

最后,再次重启你的服务器,刷新浏览器。现在,你终于看见debug文件了。

Rails和Rulers都很难调试。在第三章我们将看到Bundler的:path选项,可以让我们调试容易 点。现在你需要重新安装gem和重启rack服务器来让新的Rulers代码执行。

这样当方便的方式行不通的时候,你也知道怎么用笨方法。

练习2. 你的库的库

你可以开始一个简单的可复用的库,就像ActiveSupport。当程序使用你的Rulers gem,然后require它(require rulers),程序会自动得到那个文件里所有方法。试试添加lib/rulsers/array.rb的文件, 然后加入以下代码:

# rulers/lib/array.rb
class Array
  def sum(start = 0)
    inject(start, &:+)
  end
end

你以前见过&:+吗?它是个有趣的技巧。:+意思是“symbol +”;就像“:foo”,意思是“symbol foo”。“&”意思是“当作块传递”-- 在圆括号里的代码块通常跟在后面。所以你正把符号当作代码块 来传递。Ruby知道把符号转换为proc,然后调用它。当你用“加”的时候,你得到“把这些加 起来”。

现在,把require 'rulers/array'添加到lib/rulers.rb的顶部。它会在所有Rulers应用里包含它。

在你打包gem前,你需要进入rulers目录,运行

$ git add -A
$ $ gem build rulers
$ gem install rulers-0.0.1.gem

这是因为rulers.gemspec实际调用git来查找包含进你gem里的文件。你可以在rulers.gemspec文件里看到一行:

gem.files = `git ls-files`.split($/)

git ls-files仅仅显示git知道的文件 -- split只是为了得到输出的每一行。

假如你创建了一个新文件,确信你重新打包前运行了git add -A,否则你看不到它!

现在有了新的rulers/array.rb文件,任何包含了Rulers的程序都能通过 [1, 2, 37, 9].sum 的方式 得到数组的和。

继续,添加更多的那些使用你的框架的程序可能会有用的方法。


以上是关于构建自己的MVC框架(Ruby语言实现)-- 第一章 从零到“它工作了!”的主要内容,如果未能解决你的问题,请参考以下文章

如何实现MVC模式

AsMVC:一个简单的MVC框架的Java实现

Go语言框架:Beego vs Gin 的区别

开发自己的PHP MVC框架

实现自己的MVC AJAX框架计划

跨语言RPC框架Thrift详解