构建自己的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版本管理工具rvm
或 rbenv
来帮你完成这些事情。)
使用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
。真正的Rails
gem仅有很少的代码。相反,它分发给其他的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语言实现)-- 第一章 从零到“它工作了!”的主要内容,如果未能解决你的问题,请参考以下文章